<!-- 博客 - 页面布局 > 上中下 -->



<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=dark>
<!-- 博客 - 导航栏上方 -->


<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/blog/img/fluid.png">
  <link rel="icon" href="/blog/img/icon/dw.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="Memory">
  <meta name="keywords" content="">

  
    <meta name="description" content="🍨 记录我的Spring Boot之旅，从困境到胜利，与你分享不平凡的成长历程">
<meta property="og:type" content="article">
<meta property="og:title" content="凤凰涅槃：Spring Boot 开发之路上的坎坷与成长">
<meta property="og:url" content="https://test.atomgit.net/blog/2023/07/06/%E5%87%A4%E5%87%B0%E6%B6%85%E6%A7%83%EF%BC%9ASpring%20Boot%20%E5%BC%80%E5%8F%91%E4%B9%8B%E8%B7%AF%E4%B8%8A%E7%9A%84%E5%9D%8E%E5%9D%B7%E4%B8%8E%E6%88%90%E9%95%BF/index.html">
<meta property="og:site_name" content="Memory&#39;s blog">
<meta property="og:description" content="🍨 记录我的Spring Boot之旅，从困境到胜利，与你分享不平凡的成长历程">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://test.atomgit.net/blog/img/artical/pathway0716.jpg">
<meta property="article:published_time" content="2023-07-06T10:04:25.000Z">
<meta property="article:modified_time" content="2025-08-04T10:18:54.000Z">
<meta property="article:author" content="Memory">
<meta property="article:tag" content="经验">
<meta property="article:tag" content="Redis">
<meta property="article:tag" content="项目">
<meta property="article:tag" content="SpringBoot">
<meta property="article:tag" content="Mybatis">
<meta property="article:tag" content="阿里云OSS">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://test.atomgit.net/blog/img/artical/pathway0716.jpg">
  
  
  
  <title>凤凰涅槃：Spring Boot 开发之路上的坎坷与成长 - Memory&#39;s blog</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/blog/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/blog/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/blog/css/highlight-dark.css" />
  



  
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">



  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"test.atomgit.net","root":"/blog/","version":"1.9.4","typing":{"enable":true,"typeSpeed":100,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":false,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"appId":"T1hUHH9Ks1ggG9DBE3HgUPpV-gzGzoHsz","appKey":"XFkgzzvzNo3J3uNCbBiSOFKc","server_url":"https://t1huhh9k.lc-cn-n1-shared.com","path":"window.location.pathname","ignore_local":false}},"search_path":"/blog/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/blog/js/utils.js" ></script>
  <script  src="/blog/js/color-schema.js" ></script>
  

  

  

  

  

  

  

  



  
<meta name="generator" content="Hexo 5.4.2"></head>

<body>
  

  <header>
    <!-- 博客 - 导航栏上方 -->


<div class="header-inner" style="height: 100vh">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/blog/">
      <strong>Memory&#39;s blog</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/">
                <i class="iconfont icon-home-fill"></i>
                <span>首页</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/archives/">
                <i class="iconfont icon-archive-fill"></i>
                <span>归档</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/categories/">
                <i class="iconfont icon-category-fill"></i>
                <span>分类</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/tags/">
                <i class="iconfont icon-tags-fill"></i>
                <span>标签</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/about/">
                <i class="iconfont icon-user-fill"></i>
                <span>关于</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/links/">
                <i class="iconfont icon-link-fill"></i>
                <span>友链</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/playlist/">
                <i class="iconfont icon-music"></i>
                <span>音乐</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/Memos/">
                <i class="iconfont icon-comment "></i>
                <span>说说</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/App/">
                <i class="iconfont icon-mobile"></i>
                <span>咫尺</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" target="_self" href="javascript:;" role="button"
                 data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                <i class="iconfont icon-books"></i>
                <span>青简</span>
              </a>
              <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                
                  
                  
                  
                  <a class="dropdown-item" target="_blank" rel="noopener" href="http://47.93.135.62:5230/explore">
                    <i class="iconfont icon-images"></i>
                    <span>碎念微光</span>
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/blog/New/">
                    <i class="iconfont icon-bug"></i>
                    <span>匠册详析</span>
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/blog/New/">
                    <i class="iconfont icon-xbox-fill"></i>
                    <span>窗景新编</span>
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/blog/New/">
                    <i class="iconfont icon-steam"></i>
                    <span>戏码幕后</span>
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/blog/New/">
                    <i class="iconfont icon-map"></i>
                    <span>他山随拾</span>
                  </a>
                
              </div>
            </li>
          
        
        
            <li class="nav-item" id="search-btn">
                <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalNotice" aria-label="Search">
                    <i class="iconfont icon-book"></i>
                    <span>公告</span>
                </a>
            </li>
            
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              <i class="iconfont icon-search"></i>
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">
              <i class="iconfont icon-dark" id="color-toggle-icon"></i>
            </a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>
 
<div id="banner" class="banner" parallax=true
     style="background: url('/blog/img/artical/pathway0716.jpg') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">

    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">

        <div class="h2">
          
            <span id="subtitle" data-typed-text="凤凰涅槃：Spring Boot 开发之路上的坎坷与成长"></span>
          
        </div>


        
          <!-- 文章页面 - 顶部信息 -->

<div class="mt-3">
  
  <span class="post-meta mr-2">
    <i class="iconfont icon-author" aria-hidden="true"></i>
    Memory
  </span>
   
  <span class="post-meta">
    <i class="iconfont icon-date-fill" aria-hidden="true"></i>
    <time datetime="2023-07-06 18:04" pubdate>
      2023年7月6日 晚上
    </time>
  </span>
  
</div>

<div class="mt-1">
  
  <span class="post-meta mr-2">
    <i class="iconfont icon-chart"></i>
     118k 字 
  </span>
   
  <span class="post-meta mr-2">
    <i class="iconfont icon-clock-fill"></i>
       295 分钟 
  </span>
     
  <span
    id="leancloud-page-views-container"
    class="post-meta"
    style="display: none"
  >
    <i class="iconfont icon-eye" aria-hidden="true"></i>
    <span id="leancloud-page-views"></span> 次
  </span>

    
</div>


        

        <!-- 添加网站运行时间 -->
        <div class="footer-content" style="background-color: transparent;">
          <div>
            <span id="timeDate"></span>
            <span id="times"></span>
            <script>
            var now = new Date();
            function createtime(){
                var grt= new Date("03/07/2023 00:00:00");//此处修改你的建站时间或者网站上线时间
                now.setTime(now.getTime()+250);
                days = (now - grt ) / 1000 / 60 / 60 / 24;
                dnum = Math.floor(days);
                hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum);
                hnum = Math.floor(hours);
                if(String(hnum).length ==1 ){
                    hnum = "0" + hnum;
                }
                minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum);
                mnum = Math.floor(minutes);
                if(String(mnum).length ==1 ){
                          mnum = "0" + mnum;
                }
                seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
                snum = Math.round(seconds);
                if(String(snum).length ==1 ){
                          snum = "0" + snum;
                }
                document.getElementById("timeDate").innerHTML = "🚀 &nbsp"+dnum+"&nbsp天";  //此次自定义显示内容
                document.getElementById("times").innerHTML = hnum + "&nbsp小时&nbsp" + mnum + "&nbsp分&nbsp" + snum + "&nbsp秒";
            }  

            if (!window.location.href.includes('/20')) {  
            setInterval("createtime()", 250);  // 只有当URL不包含'specific-page'时，才调用createtime  
            }//此次自定义显示内容
            </script>
          </div>
        </div>

      </div>
      
        <div class="scroll-down-bar">
          <i class="iconfont icon-arrowdown"></i>
        </div>
      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      <!-- 文章页  -->


<div class="container-fluid nopadding-x">

  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar category-bar" style="margin-right: -1rem">
    <!-- 文章页面 - 左边栏所属分类 -->
  

<!-- 文章页 - 文章分类 -->





<div class="category-list">
  
  
    
    
    
    <div class="category row nomargin-x">
      <a class="category-item 
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="代码锋芒"
        id="heading-843238cb7c4b39b52e95311733ff6da6" role="tab" data-toggle="collapse" href="#collapse-843238cb7c4b39b52e95311733ff6da6"
        aria-expanded="true"
      >
        代码锋芒
        <span class="list-group-count">(31)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse show" id="collapse-843238cb7c4b39b52e95311733ff6da6"
           role="tabpanel" aria-labelledby="heading-843238cb7c4b39b52e95311733ff6da6">
        
        
          
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2023/03/19/MySQL%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE/" title="MySQL安装配置"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">MySQL安装配置</span>
        </a>
      
    
      
      
        <a href="/blog/2023/03/22/MySQL%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/" title="MySQL从入门到入土"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">MySQL从入门到入土</span>
        </a>
      
    
      
      
        <a href="/blog/2023/04/09/Java%E6%B3%9B%E5%9E%8B%E7%9A%84%E7%90%86%E8%A7%A3/" title="Java泛型：赋予灵活性的利器"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">Java泛型：赋予灵活性的利器</span>
        </a>
      
    
      
      
        <a href="/blog/2023/05/21/%E6%8E%A2%E7%B4%A2%20Python%20%E7%88%AC%E8%99%AB%E7%9A%84%E6%97%A0%E9%99%90%E5%8F%AF%E8%83%BD%E6%80%A7%EF%BC%9A%E5%9F%BA%E7%A1%80%E4%B8%8E%E5%BA%94%E7%94%A8/" title="探索Python爬虫的无限可能性：基础与应用"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">探索Python爬虫的无限可能性：基础与应用</span>
        </a>
      
    
      
      
        <a href="/blog/2023/06/03/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%9A%84%20JavaWeb%20%E4%B9%8B%E6%97%85/" title="从零开始的 JavaWeb 之旅"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">从零开始的 JavaWeb 之旅</span>
        </a>
      
    
      
      
        <a href="/blog/2023/06/10/%E6%95%B0%E6%8D%AE%E5%BA%93-%E5%AE%8C%E6%95%B4%E6%80%A7%E7%BA%A6%E6%9D%9F/" title="数据库-完整性约束"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">数据库-完整性约束</span>
        </a>
      
    
      
      
        <a href="/blog/2023/06/24/%E4%BD%BF%E7%94%A8%20JSP%20%E6%9E%84%E5%BB%BA%20JavaWeb%20%E9%A1%B9%E7%9B%AE-%E4%BB%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E5%A4%A7%E4%BD%9C%E4%B8%9A%E4%B8%BA%E4%BE%8B/" title="使用JSP构建JavaWeb项目--以数据库大作业为例"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">使用JSP构建JavaWeb项目--以数据库大作业为例</span>
        </a>
      
    
      
      
        <a href="/blog/2023/07/06/%E6%89%93%E9%80%A0%E6%B5%81%E7%95%85%E7%BC%96%E7%A0%81%E4%B9%8B%E6%97%85%EF%BC%9AIntelliJ%20IDEA%20%E7%9A%84%E9%AB%98%E6%95%88%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7%E4%B8%8E%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E4%BC%98%E5%8C%96/" title="打造流畅编码之旅：IntelliJ IDEA 的高效使用技巧与工作流程优化"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">打造流畅编码之旅：IntelliJ IDEA 的高效使用技巧与工作流程优化</span>
        </a>
      
    
      
      
        <a href="/blog/2023/07/06/%E5%87%A4%E5%87%B0%E6%B6%85%E6%A7%83%EF%BC%9ASpring%20Boot%20%E5%BC%80%E5%8F%91%E4%B9%8B%E8%B7%AF%E4%B8%8A%E7%9A%84%E5%9D%8E%E5%9D%B7%E4%B8%8E%E6%88%90%E9%95%BF/" title="凤凰涅槃：Spring Boot 开发之路上的坎坷与成长"
           class="list-group-item list-group-item-action
           active">
          <span class="category-post">凤凰涅槃：Spring Boot 开发之路上的坎坷与成长</span>
        </a>
      
    
      
      
        <a href="/blog/2023/07/11/%E4%BB%8EIP%E5%88%B0Ping%E5%91%BD%E4%BB%A4%EF%BC%9A%E4%BA%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E8%BF%9E%E6%8E%A5%E7%9A%84%E5%9F%BA%E7%A1%80/" title="从IP到Ping命令：了解计算机网络连接的基础"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">从IP到Ping命令：了解计算机网络连接的基础</span>
        </a>
      
    
      
      
        <a href="/blog/categories/%E4%BB%A3%E7%A0%81%E9%94%8B%E8%8A%92/" class="list-group-item list-group-item-action">
          <span class="category-post">More...</span>
        </a>
        
  </div>

          
  
    
    
    
    <div class="category-sub row nomargin-x">
      <a class="category-subitem collapsed
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="职路星火"
        id="heading-59c550454d1ecf67a2ead26a1c46d14b" role="tab" data-toggle="collapse" href="#collapse-59c550454d1ecf67a2ead26a1c46d14b"
        aria-expanded="false"
      >
        职路星火
        <span class="list-group-count">(2)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse " id="collapse-59c550454d1ecf67a2ead26a1c46d14b"
           role="tabpanel" aria-labelledby="heading-59c550454d1ecf67a2ead26a1c46d14b">
        
        
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2024/03/07/%E7%A0%BA%E5%89%91%E5%87%BA%E9%9E%98%EF%BC%9A%E6%88%91%E7%9A%84%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%B1%82%E8%81%8C%E4%B9%8B%E6%97%85/" title="砺剑出鞘：我的软件工程师求职之旅"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">砺剑出鞘：我的软件工程师求职之旅</span>
        </a>
      
    
      
      
        <a href="/blog/2024/04/16/%E6%8B%A8%E4%BA%91%E8%A7%81%E6%97%A5%EF%BC%9A%E8%81%8C%E5%9C%BA%E7%94%9F%E6%B4%BB%E7%9A%84%E7%82%B9%E6%BB%B4%E8%AE%B0%E5%BD%95/" title="拨云见日：职场生活的点滴记录"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">拨云见日：职场生活的点滴记录</span>
        </a>
      
    
  </div>

        
      </div>
    </div>
  
    
    
    
    <div class="category-sub row nomargin-x">
      <a class="category-subitem collapsed
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="部署之道"
        id="heading-36acf247b1a64f4bbe423776a392cfde" role="tab" data-toggle="collapse" href="#collapse-36acf247b1a64f4bbe423776a392cfde"
        aria-expanded="false"
      >
        部署之道
        <span class="list-group-count">(5)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse " id="collapse-36acf247b1a64f4bbe423776a392cfde"
           role="tabpanel" aria-labelledby="heading-36acf247b1a64f4bbe423776a392cfde">
        
        
          
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2023/06/22/Nginx%20%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97%EF%BC%9A%E7%90%86%E8%A7%A3%E3%80%81%E9%85%8D%E7%BD%AE%E5%92%8C%E4%BC%98%E5%8C%96/" title="Nginx 入门指南：理解、配置和优化"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">Nginx 入门指南：理解、配置和优化</span>
        </a>
      
    
      
      
        <a href="/blog/2023/07/11/Docker%20%E5%85%A5%E9%97%A8%E4%B8%8E%E5%AE%9E%E8%B7%B5%EF%BC%9A%E5%BF%AB%E9%80%9F%E6%89%93%E9%80%A0%E9%AB%98%E6%95%88%E7%BD%91%E7%AB%99%E9%83%A8%E7%BD%B2%E7%8E%AF%E5%A2%83/" title="Docker入门与实践：快速打造高效网站部署环境"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">Docker入门与实践：快速打造高效网站部署环境</span>
        </a>
      
    
      
      
        <a href="/blog/2023/08/17/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20API%20%E7%BD%91%E5%85%B3%EF%BC%9A%E5%8E%9F%E7%90%86%E3%80%81%E5%8A%9F%E8%83%BD%E5%92%8C%E7%94%A8%E9%80%94/" title="深入理解 API 网关：原理、功能和用途"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">深入理解 API 网关：原理、功能和用途</span>
        </a>
      
    
  </div>

          
  
    
    
    
    <div class="category-sub row nomargin-x">
      <a class="category-subitem collapsed
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="职路星火"
        id="heading-59c550454d1ecf67a2ead26a1c46d14b" role="tab" data-toggle="collapse" href="#collapse-59c550454d1ecf67a2ead26a1c46d14b"
        aria-expanded="false"
      >
        职路星火
        <span class="list-group-count">(2)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse " id="collapse-59c550454d1ecf67a2ead26a1c46d14b"
           role="tabpanel" aria-labelledby="heading-59c550454d1ecf67a2ead26a1c46d14b">
        
        
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2024/12/16/%E6%BC%AB%E6%BC%AB%E5%BE%81%E9%80%94%EF%BC%9A%E7%AD%91%E6%A2%A6%E4%BA%8E%E8%81%8C%E5%9C%BA%E9%A3%8E%E9%9B%A8%EF%BC%8C%E5%90%9F%E5%92%8F%E4%BA%8E%E6%97%A5%E5%B8%B8%E8%AF%97%E6%84%8F/" title="漫漫征途：筑梦于职场风雨，吟咏于日常诗意"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">漫漫征途：筑梦于职场风雨，吟咏于日常诗意</span>
        </a>
      
    
      
      
        <a href="/blog/2025/10/23/%E4%BF%9D%E9%A9%BE%E6%8A%A4%E8%88%AA%E2%80%94%E2%80%94%E5%90%8E%E5%8F%B0%E8%BF%90%E7%BB%B4%E8%A7%86%E8%A7%92%E4%B8%8B%E7%9A%84%E9%AB%98%E9%80%9F%E5%85%AC%E8%B7%AF%E2%80%9C%E7%94%9F%E5%91%BD%E7%BA%BF%E2%80%9D/" title="保驾护航——后台运维视角下的高速公路“生命线”"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">保驾护航——后台运维视角下的高速公路“生命线”</span>
        </a>
      
    
  </div>

        
      </div>
    </div>
  
        
      </div>
    </div>
  
    
    
    
    <div class="category-sub row nomargin-x">
      <a class="category-subitem collapsed
          list-group-item category-item-action col-10 col-md-11 col-xm-11" title="项目志录"
        id="heading-005e56101bc2a873707cb4461a70ded8" role="tab" data-toggle="collapse" href="#collapse-005e56101bc2a873707cb4461a70ded8"
        aria-expanded="false"
      >
        项目志录
        <span class="list-group-count">(1)</span>
        <i class="iconfont icon-arrowright"></i>
      </a>
      
      <div class="category-collapse collapse " id="collapse-005e56101bc2a873707cb4461a70ded8"
           role="tabpanel" aria-labelledby="heading-005e56101bc2a873707cb4461a70ded8">
        
        
          
  <div class="category-post-list">
    
    
      
      
        <a href="/blog/2023/07/30/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB%EF%BC%9A%E8%B8%A9%E5%9D%91%E6%8C%87%E5%8D%97%E4%B8%8E%E6%8A%80%E5%B7%A7%E6%80%BB%E7%BB%93/" title="小程序开发经验分享：踩坑指南与技巧总结"
           class="list-group-item list-group-item-action
           ">
          <span class="category-post">小程序开发经验分享：踩坑指南与技巧总结</span>
        </a>
      
    
  </div>

        
      </div>
    </div>
  
        
      </div>
    </div>
  
</div>

  </aside>


    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">凤凰涅槃：Spring Boot 开发之路上的坎坷与成长</h1>
            
              <p class="note note-success">
                
                  
                    本文最后更新于：4 个月前
                  
                
              </p>
            
            
              <div class="markdown-body">
                
                <h1 id="破冰"><a href="#破冰" class="headerlink" title="破冰"></a>破冰</h1><h2 id="写作目标"><a href="#写作目标" class="headerlink" title="写作目标"></a>写作目标</h2><p>今天是开发壁纸网站 memory-icon 的第一天，本来没打算写什么开发文档的，太耗时间，整体实现思路也简单，就不写了</p>
<p>但是有必要记录一下：从零开发一个 SpringBoot 项目，至少应该要做些什么</p>
<p>这就算给定制自己的 SpringBoot 模板开个好头了（2023/08/01 午）</p>
<h2 id="开发思路"><a href="#开发思路" class="headerlink" title="开发思路"></a>开发思路</h2><p><strong>从零开发一个 SpringBoot 要做什么？一定是做到以下几点：（2023/08/01 午）</strong></p>
<ul>
<li><p>新建模块</p>
</li>
<li><p>导入必要的依赖：MySQL、Mybatis、MybatisPlus、commons-lang3、lombok、Gson 等</p>
</li>
<li><p>新增必要 yaml 配置：项目名、启动端口、上下文路径、数据库地址</p>
</li>
<li><p>新增 banner.txt</p>
</li>
<li><p>新增数据库表，连接数据库，保存建表语句</p>
</li>
<li><p>新增 controller 层、service 层</p>
</li>
<li><p>利用 MybatisX-Generator 插件，快速生成 domain、mapper、service、XXXMaper.xml</p>
</li>
<li><p>新增 config 层：CorsConfig、MybatisPlusConfig、Knife4jConfig、JsonConfig 等</p>
</li>
</ul>
<h1 id="思维碰撞"><a href="#思维碰撞" class="headerlink" title="思维碰撞"></a>思维碰撞</h1><h2 id="着手开发"><a href="#着手开发" class="headerlink" title="着手开发"></a>着手开发</h2><h3 id="快速编写-Controller-层"><a href="#快速编写-Controller-层" class="headerlink" title="快速编写 Controller 层"></a>快速编写 Controller 层</h3><ul>
<li><h5 id="什么是-Controller-层？（2023-08-14-早）"><a href="#什么是-Controller-层？（2023-08-14-早）" class="headerlink" title="什么是 Controller 层？（2023/08/14 早）"></a>什么是 Controller 层？（2023/08/14 早）</h5></li>
</ul>
<figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs livecodeserver">在Web开发中，Controller层是MVC（模型-视图-控制器）架构的一部分，用于接收并处理前端请求。<br>具体来说，Controller层负责以下几个任务：<br>	<span class="hljs-number">1.</span>接收HTTP请求：Controller通过定义特定的路由（<span class="hljs-built_in">URL</span>映射）来接收前端发送的HTTP请求。<br>	<span class="hljs-number">2.</span>处理请求：Controller中的方法将根据请求的类型、路径以及请求参数等信息，执行相应的逻辑操作。<br>	<span class="hljs-number">3.</span>调用服务层：在Controller中，可以调用服务层（Service层、业务层）的方法来实现相关业务逻辑。<br>	<span class="hljs-number">4.</span>返回响应：在完成对请求的处理后，Controller负责将数据或者视图响应返回给前端。<br>Controller层的目标是实现请求-响应的流程控制，将请求按照业务逻辑分配给对应的服务层处理，并将执行结果返回给前端。同时，Controller层还可以处理一些与业务逻辑相关的验证或者数据转换等操作。<br></code></pre></td></tr></table></figure>

<ul>
<li>引入依赖：</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.7.14<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>		<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="这里贴个小技巧，alt-insert-快速添加依赖模板："><a href="#这里贴个小技巧，alt-insert-快速添加依赖模板：" class="headerlink" title="这里贴个小技巧，alt + insert 快速添加依赖模板："></a>这里贴个小技巧，alt + insert 快速添加依赖模板：</h5></li>
</ul>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230814130719252.png" alt="image-20230814130719252"></p>
<ul>
<li><h5 id="新增-controller-层，编写响应请求的逻辑："><a href="#新增-controller-层，编写响应请求的逻辑：" class="headerlink" title="新增 controller 层，编写响应请求的逻辑："></a>新增 controller 层，编写响应请求的逻辑：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> 邓哈哈</span><br><span class="hljs-comment"> * 2023/8/14 12:06</span><br><span class="hljs-comment"> * Function:</span><br><span class="hljs-comment"> * Version 1.0</span><br><span class="hljs-comment"> */</span><br><br><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;/test&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TestController</span> &#123;<br>    <span class="hljs-meta">@GetMapping(&quot;/name&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getName</span><span class="hljs-params">(String name)</span> &#123;<br>        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">&quot;我的名字是: %s&quot;</span>, name);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="编辑-application-yaml-配置文件："><a href="#编辑-application-yaml-配置文件：" class="headerlink" title="编辑 application.yaml 配置文件："></a>编辑 application.yaml 配置文件：</h5></li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">server:</span><br>  <span class="hljs-attr">port:</span> <span class="hljs-number">8088</span><br>  <span class="hljs-attr">servlet:</span><br>    <span class="hljs-attr">context-path:</span> <span class="hljs-string">/api</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="发送请求，测试接口"><a href="#发送请求，测试接口" class="headerlink" title="发送请求，测试接口"></a>发送请求，测试接口</h5></li>
<li><p>这一步的操作方法有很多：</p>
<ul>
<li>Postman 工具</li>
<li>Swagger + Knif4j 接口文档</li>
<li>浏览器</li>
<li>IDEA 模拟浏览器</li>
</ul>
</li>
<li><h5 id="有关-Postman-工具的使用，这里不再详述"><a href="#有关-Postman-工具的使用，这里不再详述" class="headerlink" title="有关 Postman 工具的使用，这里不再详述"></a>有关 Postman 工具的使用，这里不再详述</h5></li>
<li><h5 id="有关快速生成-Swagger-Knif4j-接口文档的教程，可以在下面-👇-的《快速生成-Swagger-Knif4j-接口文档》栏目中了解到"><a href="#有关快速生成-Swagger-Knif4j-接口文档的教程，可以在下面-👇-的《快速生成-Swagger-Knif4j-接口文档》栏目中了解到" class="headerlink" title="有关快速生成 Swagger + Knif4j 接口文档的教程，可以在下面 👇 的《快速生成 Swagger + Knif4j 接口文档》栏目中了解到"></a>有关快速生成 Swagger + Knif4j 接口文档的教程，可以在下面 👇 的《快速生成 Swagger + Knif4j 接口文档》栏目中了解到</h5></li>
<li><h5 id="有关-IDEA-如何模拟浏览器，发送-HTTP-请求，可以在《掌握-JetBrains-IntelliJ-IDEA：使用心得与技巧》一文中的《模拟浏览器发送请求》栏目中找到答案"><a href="#有关-IDEA-如何模拟浏览器，发送-HTTP-请求，可以在《掌握-JetBrains-IntelliJ-IDEA：使用心得与技巧》一文中的《模拟浏览器发送请求》栏目中找到答案" class="headerlink" title="有关 IDEA 如何模拟浏览器，发送 HTTP 请求，可以在《掌握-JetBrains-IntelliJ-IDEA：使用心得与技巧》一文中的《模拟浏览器发送请求》栏目中找到答案"></a>有关 IDEA 如何模拟浏览器，发送 HTTP 请求，可以在《掌握-JetBrains-IntelliJ-IDEA：使用心得与技巧》一文中的《模拟浏览器发送请求》栏目中找到答案</h5></li>
<li><h5 id="有关-SpringMVC-处理-HTTP-请求参数的方式，可以在《SpringBoot-配置》一文中的-《SpringMVC-请求参数的处理》栏目中找到答案"><a href="#有关-SpringMVC-处理-HTTP-请求参数的方式，可以在《SpringBoot-配置》一文中的-《SpringMVC-请求参数的处理》栏目中找到答案" class="headerlink" title="有关 SpringMVC 处理 HTTP 请求参数的方式，可以在《SpringBoot 配置》一文中的 《SpringMVC 请求参数的处理》栏目中找到答案"></a>有关 SpringMVC 处理 HTTP 请求参数的方式，可以在《SpringBoot 配置》一文中的 《SpringMVC 请求参数的处理》栏目中找到答案</h5></li>
<li><p>浏览器发送请求，结果如下图所示：（2023/08/14 午）</p>
</li>
</ul>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230814132007439.png" alt="image-20230814132007439"></p>
<ul>
<li>IDEA 模拟浏览器发送请求，结果如下图所示：</li>
</ul>
<figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs http">GET http://localhost:8088/api/test/name?name=邓啊呀<br></code></pre></td></tr></table></figure>

<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230814132051083.png" alt="image-20230814132051083"></p>
<h3 id="全局异常处理"><a href="#全局异常处理" class="headerlink" title="全局异常处理"></a>全局异常处理</h3><h3 id="MybatisPlus"><a href="#MybatisPlus" class="headerlink" title="MybatisPlus"></a>MybatisPlus</h3><ul>
<li><p>官方文档：<a target="_blank" rel="noopener" href="https://baomidou.com/">MyBatis-Plus (baomidou.com)</a></p>
</li>
<li><h5 id="导入依赖坐标：（2023-08-21-早）"><a href="#导入依赖坐标：（2023-08-21-早）" class="headerlink" title="导入依赖坐标：（2023/08/21 早）"></a>导入依赖坐标：（2023/08/21 早）</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>     <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.baomidou<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>     <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mybatis-plus-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>     <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.4.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br> <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li>配置:</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">mybatis-plus:</span><br>  <span class="hljs-attr">configuration:</span><br>    <span class="hljs-comment">#在映射实体或者属性时，将数据库中表名和字段名中的下划线去掉，按照驼峰命名法映射</span><br>    <span class="hljs-attr">map-underscore-to-camel-case:</span> <span class="hljs-literal">true</span><br>    <span class="hljs-attr">log-impl:</span> <span class="hljs-string">org.apache.ibatis.logging.stdout.StdOutImpl</span> <span class="hljs-comment"># 开启SQL日志</span><br>  <span class="hljs-attr">global-config:</span><br>    <span class="hljs-attr">db-config:</span><br>      <span class="hljs-attr">id-type:</span> <span class="hljs-string">ASSIGN_ID</span> <span class="hljs-comment"># 主键自增长策略</span><br>      <span class="hljs-attr">logic-delete-field:</span> <span class="hljs-string">isDelete</span> <span class="hljs-comment"># 全局逻辑删除的实体字段名</span><br>      <span class="hljs-attr">logic-delete-value:</span> <span class="hljs-number">1</span> <span class="hljs-comment"># 逻辑已删除值（默认为 1）</span><br>      <span class="hljs-attr">logic-not-delete-value:</span> <span class="hljs-number">0</span> <span class="hljs-comment"># 逻辑未删除值（默认为 0）</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="以上是最常用的配置，其中，按照驼峰命名法映射是这样处理的："><a href="#以上是最常用的配置，其中，按照驼峰命名法映射是这样处理的：" class="headerlink" title="以上是最常用的配置，其中，按照驼峰命名法映射是这样处理的："></a>以上是最常用的配置，其中，按照驼峰命名法映射是这样处理的：</h5><ul>
<li><p>建库建表后，如果表字段是下划线命名的，而实体类是驼峰命名，如下：</p>
</li>
<li><p><img src="http://blog.memory-life.xyz/image-20230821115235186.png" alt="image-20230821115235186"></p>
</li>
<li><h5 id="当我们操作数据库时，MybatisPlus-会将驼峰命名的实体类属性，映射为下划线命名的表字段了："><a href="#当我们操作数据库时，MybatisPlus-会将驼峰命名的实体类属性，映射为下划线命名的表字段了：" class="headerlink" title="当我们操作数据库时，MybatisPlus 会将驼峰命名的实体类属性，映射为下划线命名的表字段了："></a>当我们操作数据库时，MybatisPlus 会将驼峰命名的实体类属性，映射为下划线命名的表字段了：</h5></li>
<li><p>```java<br>boolean updateById = setMealService.updateById(setmeal);</p>
<figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs clean"><br>- ##### 开启 SQL 日志不用多说，方便代码调试和分析报错信息<br><br>- ##### 主键自增长策略，也可以在主键字段上添加注解：<br><br>```java<br>  <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 主键</span><br><span class="hljs-comment">     */</span><br>    @TableId(value = <span class="hljs-string">&quot;id&quot;</span>, type = IdType.AUTO)<br>    private Long id;<br></code></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>逻辑删除，被删除的数据记录不会直接从表中删除，而是在指定字段上标注为已删除，需要在字段上添加以下注解：</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">   * 是否删除(0-未删, 1-已删)</span><br><span class="hljs-comment">   */</span><br>  <span class="hljs-meta">@TableField(value = &quot;isDelete&quot;)</span><br>  <span class="hljs-meta">@TableLogic</span><br>  <span class="hljs-keyword">private</span> Integer isDelete;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="最后在-WebConfig-下配置分页插件：（2023-08-22-早）"><a href="#最后在-WebConfig-下配置分页插件：（2023-08-22-早）" class="headerlink" title="最后在 WebConfig 下配置分页插件：（2023/08/22 早）"></a>最后在 WebConfig 下配置分页插件：（2023/08/22 早）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Web配置类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WebConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 分页插件（官网最新）</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> MybatisPlusInterceptor <span class="hljs-title function_">mybatisPlusInterceptor</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-type">MybatisPlusInterceptor</span> <span class="hljs-variable">interceptor</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MybatisPlusInterceptor</span>();<br>        interceptor.addInnerInterceptor(<span class="hljs-keyword">new</span> <span class="hljs-title class_">PaginationInnerInterceptor</span>(DbType.MYSQL));<br>        <span class="hljs-keyword">return</span> interceptor;<br>    &#125;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="对象映射器（消息转换器）"><a href="#对象映射器（消息转换器）" class="headerlink" title="对象映射器（消息转换器）"></a>对象映射器（消息转换器）</h3><ul>
<li><h5 id="xxxxxxxxxx-4-0-0-com-memory-api-memory-api-0-0-1-SNAPSHOT-pom-xml-​-com-memory-client-memory-client-0-0-1-SNAPSHOT​-com-memory-common-memory-common-0-0-1-SNAPSHOT-com-belerweb-pinyin4j-2-5-1-com-memory-memory-client-spring-boot-starter-0-0-1-​-org-springframework-boot-spring-boot-maven-plugin-repackage-repackage-​​xml"><a href="#xxxxxxxxxx-4-0-0-com-memory-api-memory-api-0-0-1-SNAPSHOT-pom-xml-​-com-memory-client-memory-client-0-0-1-SNAPSHOT​-com-memory-common-memory-common-0-0-1-SNAPSHOT-com-belerweb-pinyin4j-2-5-1-com-memory-memory-client-spring-boot-starter-0-0-1-​-org-springframework-boot-spring-boot-maven-plugin-repackage-repackage-​​xml" class="headerlink" title="xxxxxxxxxx     4.0.0            com.memory.api        memory-api        0.0.1-SNAPSHOT        ../pom.xml    ​    com.memory.client    memory-client    0.0.1-SNAPSHOT​                        com.memory.common            memory-common            0.0.1-SNAPSHOT                            com.belerweb            pinyin4j            2.5.1                             com.memory            memory-client-spring-boot-starter            0.0.1            ​                                        org.springframework.boot                spring-boot-maven-plugin                                                            repackage                                                    repackage                                                                                    ​​xml"></a>xxxxxxxxxx <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>com.memory.api</groupId>        <artifactId>memory-api</artifactId>        <version>0.0.1-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>​    <groupId>com.memory.client</groupId>    <artifactId>memory-client</artifactId>    <version>0.0.1-SNAPSHOT</version>​    <dependencies>        <dependency>            <groupId>com.memory.common</groupId>            <artifactId>memory-common</artifactId>            <version>0.0.1-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>com.belerweb</groupId>            <artifactId>pinyin4j</artifactId>            <version>2.5.1</version> <!-- 使用时检查是否有更新的版本 -->        </dependency>        <dependency>            <groupId>com.memory</groupId>            <artifactId>memory-client-spring-boot-starter</artifactId>            <version>0.0.1</version>        </dependency>    </dependencies>​    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <executions>                    <execution>                        <id>repackage</id>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build>​</project>​xml</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 对象映射器:基于jackson将Java对象转为json，或者将json转为Java对象</span><br><span class="hljs-comment"> * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]</span><br><span class="hljs-comment"> * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JacksonObjectMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ObjectMapper</span> &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">DEFAULT_DATE_FORMAT</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;yyyy-MM-dd&quot;</span>;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">DEFAULT_DATE_TIME_FORMAT</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">DEFAULT_TIME_FORMAT</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;HH:mm:ss&quot;</span>;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">JacksonObjectMapper</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-built_in">super</span>();<br>        <span class="hljs-comment">//收到未知属性时不报异常</span><br>        <span class="hljs-built_in">this</span>.configure(FAIL_ON_UNKNOWN_PROPERTIES, <span class="hljs-literal">false</span>);<br><br>        <span class="hljs-comment">//反序列化时，属性不存在的兼容处理</span><br>        <span class="hljs-built_in">this</span>.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);<br><br><br>        <span class="hljs-type">SimpleModule</span> <span class="hljs-variable">simpleModule</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SimpleModule</span>()<br>                .addDeserializer(LocalDateTime.class, <span class="hljs-keyword">new</span> <span class="hljs-title class_">LocalDateTimeDeserializer</span>(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))<br>                .addDeserializer(LocalDate.class, <span class="hljs-keyword">new</span> <span class="hljs-title class_">LocalDateDeserializer</span>(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))<br>                .addDeserializer(LocalTime.class, <span class="hljs-keyword">new</span> <span class="hljs-title class_">LocalTimeDeserializer</span>(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))<br><br>                .addSerializer(BigInteger.class, ToStringSerializer.instance)<br>                .addSerializer(Long.class, ToStringSerializer.instance)<br>                .addSerializer(LocalDateTime.class, <span class="hljs-keyword">new</span> <span class="hljs-title class_">LocalDateTimeSerializer</span>(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))<br>                .addSerializer(LocalDate.class, <span class="hljs-keyword">new</span> <span class="hljs-title class_">LocalDateSerializer</span>(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))<br>                .addSerializer(LocalTime.class, <span class="hljs-keyword">new</span> <span class="hljs-title class_">LocalTimeSerializer</span>(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));<br><br>        <span class="hljs-comment">//注册功能模块 例如，可以添加自定义序列化器和反序列化器</span><br>        <span class="hljs-built_in">this</span>.registerModule(simpleModule);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="在-WebConfig-下作如下配置："><a href="#在-WebConfig-下作如下配置：" class="headerlink" title="在 WebConfig 下作如下配置："></a>在 WebConfig 下作如下配置：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Web配置类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WebConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 扩展消息转换器</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> converters converters</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">extendMessageConverters</span><span class="hljs-params">(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters)</span> &#123;<br>        log.info(<span class="hljs-string">&quot;扩展消息转换器...&quot;</span>);<br>        <span class="hljs-comment">//创建消息转换器</span><br>        <span class="hljs-type">MappingJackson2HttpMessageConverter</span> <span class="hljs-variable">messageConverter</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MappingJackson2HttpMessageConverter</span>();<br>        <span class="hljs-comment">//设置具体的对象映射器</span><br>        messageConverter.setObjectMapper(<span class="hljs-keyword">new</span> <span class="hljs-title class_">JacksonObjectMapper</span>());<br>        <span class="hljs-comment">//通过索引设置,让自己的转换器放在最前面,否则默认的jackson转换器会在最前面,用不上我们设置的转换器</span><br>        converters.add(<span class="hljs-number">0</span>, messageConverter);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="全局跨域处理"><a href="#全局跨域处理" class="headerlink" title="全局跨域处理"></a>全局跨域处理</h3><ul>
<li><h5 id="xxxxxxxxxx-4-0-0-com-memory-api-memory-api-0-0-1-SNAPSHOT-pom-xml-​-com-memory-client-memory-client-0-0-1-SNAPSHOT​-com-memory-common-memory-common-0-0-1-SNAPSHOT-com-belerweb-pinyin4j-2-5-1-com-memory-memory-client-spring-boot-starter-0-0-1-​-org-springframework-boot-spring-boot-maven-plugin-repackage-repackage-​​xml-1"><a href="#xxxxxxxxxx-4-0-0-com-memory-api-memory-api-0-0-1-SNAPSHOT-pom-xml-​-com-memory-client-memory-client-0-0-1-SNAPSHOT​-com-memory-common-memory-common-0-0-1-SNAPSHOT-com-belerweb-pinyin4j-2-5-1-com-memory-memory-client-spring-boot-starter-0-0-1-​-org-springframework-boot-spring-boot-maven-plugin-repackage-repackage-​​xml-1" class="headerlink" title="xxxxxxxxxx     4.0.0            com.memory.api        memory-api        0.0.1-SNAPSHOT        ../pom.xml    ​    com.memory.client    memory-client    0.0.1-SNAPSHOT​                        com.memory.common            memory-common            0.0.1-SNAPSHOT                            com.belerweb            pinyin4j            2.5.1                             com.memory            memory-client-spring-boot-starter            0.0.1            ​                                        org.springframework.boot                spring-boot-maven-plugin                                                            repackage                                                    repackage                                                                                    ​​xml"></a>xxxxxxxxxx <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>com.memory.api</groupId>        <artifactId>memory-api</artifactId>        <version>0.0.1-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>​    <groupId>com.memory.client</groupId>    <artifactId>memory-client</artifactId>    <version>0.0.1-SNAPSHOT</version>​    <dependencies>        <dependency>            <groupId>com.memory.common</groupId>            <artifactId>memory-common</artifactId>            <version>0.0.1-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>com.belerweb</groupId>            <artifactId>pinyin4j</artifactId>            <version>2.5.1</version> <!-- 使用时检查是否有更新的版本 -->        </dependency>        <dependency>            <groupId>com.memory</groupId>            <artifactId>memory-client-spring-boot-starter</artifactId>            <version>0.0.1</version>        </dependency>    </dependencies>​    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>                <executions>                    <execution>                        <id>repackage</id>                        <goals>                            <goal>repackage</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build>​</project>​xml</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Web配置类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WebConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 允许跨域请求</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> registry registry</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addCorsMappings</span><span class="hljs-params">(CorsRegistry registry)</span> &#123;<br>        registry.addMapping(<span class="hljs-string">&quot;/**&quot;</span>)<br>                .allowCredentials(<span class="hljs-literal">true</span>)<br>                .allowedOrigins(<span class="hljs-string">&quot;http://localhost:7070&quot;</span>, <span class="hljs-string">&quot;http://localhost:3000&quot;</span>,<span class="hljs-string">&quot;http://120.55.62.195:7071&quot;</span>)<br>                .allowedMethods(<span class="hljs-string">&quot;GET&quot;</span>, <span class="hljs-string">&quot;POST&quot;</span>, <span class="hljs-string">&quot;DELETE&quot;</span>, <span class="hljs-string">&quot;PUT&quot;</span>)<br>                .maxAge(<span class="hljs-number">3600</span>);<br>    &#125;<br>&#125;<br><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="只要是前端浏览器页面，调用后端接口，都应该在前后端做好跨域处理，不然就会遇到这种鸟问题，都不知道怎么解决：（2023-09-09-晚）"><a href="#只要是前端浏览器页面，调用后端接口，都应该在前后端做好跨域处理，不然就会遇到这种鸟问题，都不知道怎么解决：（2023-09-09-晚）" class="headerlink" title="只要是前端浏览器页面，调用后端接口，都应该在前后端做好跨域处理，不然就会遇到这种鸟问题，都不知道怎么解决：（2023/09/09 晚）"></a>只要是前端浏览器页面，调用后端接口，都应该在<strong>前后端做好跨域处理</strong>，不然就会遇到这种鸟问题，都不知道怎么解决：（2023/09/09 晚）</h5></li>
</ul>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230909174834655.png" alt="image-20230909174834655"></p>
<h3 id="Filter-全局过滤器"><a href="#Filter-全局过滤器" class="headerlink" title="Filter 全局过滤器"></a>Filter 全局过滤器</h3><ul>
<li><h5 id="年后，我学习了第一个完整的-SpringBoot-项目：瑞吉外卖，下面给出编写过滤器的一段代码：（2023-08-22-早）"><a href="#年后，我学习了第一个完整的-SpringBoot-项目：瑞吉外卖，下面给出编写过滤器的一段代码：（2023-08-22-早）" class="headerlink" title="年后，我学习了第一个完整的 SpringBoot 项目：瑞吉外卖，下面给出编写过滤器的一段代码：（2023/08/22 早）"></a>年后，我学习了第一个完整的 SpringBoot 项目：瑞吉外卖，下面给出编写过滤器的一段代码：（2023/08/22 早）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> 邓哈哈</span><br><span class="hljs-comment"> * 2023/1/13 10:19</span><br><span class="hljs-comment"> * Function:登录拦截</span><br><span class="hljs-comment"> * Version 1.0</span><br><span class="hljs-comment"> */</span><br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 检查用户/员工是否完成登录</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@WebFilter(filterName = &quot;LoginCheckFilter&quot;)</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginCheckFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Filter</span> &#123;<span class="hljs-comment">//验证是否登录</span><br>    <span class="hljs-comment">//路径匹配器,支持通配符</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">AntPathMatcher</span> <span class="hljs-variable">PATH_MATCHER</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AntPathMatcher</span>();<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doFilter</span><span class="hljs-params">(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="hljs-keyword">throws</span> IOException, ServletException &#123;<br>        <span class="hljs-type">HttpServletRequest</span> <span class="hljs-variable">request</span> <span class="hljs-operator">=</span> (HttpServletRequest) servletRequest;<br>        <span class="hljs-type">HttpServletResponse</span> <span class="hljs-variable">response</span> <span class="hljs-operator">=</span> (HttpServletResponse) servletResponse;<br>        <span class="hljs-comment">//1.获取本次请求的URI</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">requestURI</span> <span class="hljs-operator">=</span> request.getRequestURI();<br><br>        <span class="hljs-comment">//定义不需要处理的请求路径</span><br>        String[] urls = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[]&#123;<br>                <span class="hljs-string">&quot;/employee/login&quot;</span>, <span class="hljs-comment">//登录时</span><br>                <span class="hljs-string">&quot;/employee/logout&quot;</span>, <span class="hljs-comment">//登出时</span><br>                <span class="hljs-string">&quot;/backend/**&quot;</span>,<br>                <span class="hljs-string">&quot;/front/**&quot;</span>,<br>                <span class="hljs-string">&quot;/user/sendMsg&quot;</span>,<br>                <span class="hljs-string">&quot;/user/login&quot;</span><br>        &#125;;<br><br>        <span class="hljs-comment">//2.判断本次请求是否需要处理</span><br>        <span class="hljs-type">boolean</span> <span class="hljs-variable">check</span> <span class="hljs-operator">=</span> check(urls, requestURI);<br><br>        <span class="hljs-comment">//3.如果不需要处理,则直接放行</span><br>        <span class="hljs-keyword">if</span> (check) &#123;<br>            log.info(<span class="hljs-string">&quot;本次请求不需要处理...&quot;</span>);<br>            filterChain.doFilter(request, response);<span class="hljs-comment">//放行请求</span><br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br><br>        <span class="hljs-comment">// 4.1.需要处理的员工的请求,则判断登录状态,如果已经登录,则直接放行</span><br>        Long empId;<br>        <span class="hljs-keyword">if</span> ((empId = (Long) request.getSession().getAttribute(<span class="hljs-string">&quot;employee&quot;</span>)) != <span class="hljs-literal">null</span>) &#123;<span class="hljs-comment">//Session中存储着员工id(登录成功)</span><br>            log.info(<span class="hljs-string">&quot;该员工已登录,id为&#123;&#125;&quot;</span>, empId);<br><br>            BaseContext.setCurrentId(empId);<span class="hljs-comment">//ThreadLocal</span><br>            filterChain.doFilter(request, response);<span class="hljs-comment">//放行请求</span><br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br><br>        <span class="hljs-comment">//4.2.需要处理的用户的请求,则判断登录状态,如果已经登录,则直接放行</span><br>        Long userId;<br>        <span class="hljs-keyword">if</span> ((userId = (Long) request.getSession().getAttribute(<span class="hljs-string">&quot;user&quot;</span>)) != <span class="hljs-literal">null</span>) &#123;<span class="hljs-comment">//Session中存储着用户id(登录成功)</span><br>            log.info(<span class="hljs-string">&quot;该用户已登录,id为&#123;&#125;&quot;</span>, userId);<br><br>            BaseContext.setCurrentId(userId);<span class="hljs-comment">//ThreadLocal</span><br><br>            filterChain.doFilter(request, response);<span class="hljs-comment">//放行请求</span><br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br><br>        <span class="hljs-comment">//5.如果未登录,则返回登录结果,通过输出流方式向客户端页面响应数据</span><br>        log.info(<span class="hljs-string">&quot;该用户未登录...&quot;</span>);<br><br>        response.getWriter().write(JSON.toJSONString(R.error(<span class="hljs-string">&quot;NOTLOGIN&quot;</span>)));<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 路径匹配,检查本次请求是否需要放行</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> urls       :已定义的不需要处理的请求</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> requestURI :接收检查的请求</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">check</span><span class="hljs-params">(String[] urls, String requestURI)</span> &#123;<br>        <span class="hljs-keyword">for</span> (String url : urls) &#123;<br>            <span class="hljs-type">boolean</span> <span class="hljs-variable">match</span> <span class="hljs-operator">=</span> PATH_MATCHER.match(url, requestURI);<br>            <span class="hljs-keyword">if</span> (match) &#123;<span class="hljs-comment">//该请求不需要处理</span><br>                <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>            &#125;<br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<span class="hljs-comment">//该请求得处理</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">   * 路径匹配,检查本次请求是否需要放行</span><br><span class="hljs-comment">   *</span><br><span class="hljs-comment">   * <span class="hljs-doctag">@param</span> urls       :已定义的不需要处理的请求</span><br><span class="hljs-comment">   * <span class="hljs-doctag">@param</span> requestURI :接收检查的请求</span><br><span class="hljs-comment">   * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">   */</span><br>  <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">check</span><span class="hljs-params">(String[] urls, String requestURI)</span> &#123;<br>      <span class="hljs-keyword">for</span> (String url : urls) &#123;<br>          <span class="hljs-type">boolean</span> <span class="hljs-variable">match</span> <span class="hljs-operator">=</span> PATH_MATCHER.match(url, requestURI);<br>          <span class="hljs-keyword">if</span> (match) &#123;<span class="hljs-comment">//该请求不需要处理</span><br>              <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>          &#125;<br>      &#125;<br>      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<span class="hljs-comment">//该请求得处理</span><br>  &#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="这段代码的亮点包括以下几点："><a href="#这段代码的亮点包括以下几点：" class="headerlink" title="这段代码的亮点包括以下几点："></a>这段代码的亮点包括以下几点：</h5><ul>
<li>使用了@Log4j 注解，简化了日志记录的操作。通过这个注解，可以直接使用 log 变量来记录日志。</li>
<li>使用了 AntPathMatcher 来匹配请求路径，支持通配符。这样可以减少对每个请求进行完全匹配的操作，提高了效率。</li>
<li>定义了不需要处理的请求路径，使用数组来存储。这样可以在数组中添加需要排除的路径，只对数组中未包含的路径进行处理。</li>
<li>使用 check 方法来判断当前请求是否需要处理。check 方法会遍历请求路径数组，通过 AntPathMatcher 进行匹配，如果匹配成功则返回 true，否则返回 false。</li>
<li>如果当前请求需要处理，将继续调用 FilterChain 的 doFilter 方法，执行后续的过滤器或 Servlet 逻辑。如果不需要处理，则直接放行，不进行后续处理。</li>
</ul>
</li>
<li><h5 id="这样的设计可以在过滤器中对请求进行过滤，在需要处理的请求中执行相应的逻辑，而不需要处理的请求可以直接放行，提高了性能和效率。（2023-08-22-早）"><a href="#这样的设计可以在过滤器中对请求进行过滤，在需要处理的请求中执行相应的逻辑，而不需要处理的请求可以直接放行，提高了性能和效率。（2023-08-22-早）" class="headerlink" title="这样的设计可以在过滤器中对请求进行过滤，在需要处理的请求中执行相应的逻辑，而不需要处理的请求可以直接放行，提高了性能和效率。（2023/08/22 早）"></a>这样的设计可以在过滤器中对请求进行过滤，在需要处理的请求中执行相应的逻辑，而不需要处理的请求可以直接放行，提高了性能和效率。（2023/08/22 早）</h5></li>
</ul>
<h3 id="ThreadLocal"><a href="#ThreadLocal" class="headerlink" title="ThreadLocal"></a>ThreadLocal</h3><ul>
<li>从上面的代码中可以看到这样的业务流程：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 4.1.需要处理的员工的请求,则判断登录状态,如果已经登录,则直接放行</span><br>   Long empId;<br>   <span class="hljs-keyword">if</span> ((empId = (Long) request.getSession().getAttribute(<span class="hljs-string">&quot;employee&quot;</span>)) != <span class="hljs-literal">null</span>) &#123;<span class="hljs-comment">//Session中存储着员工id(登录成功)</span><br>       log.info(<span class="hljs-string">&quot;该员工已登录,id为&#123;&#125;&quot;</span>, empId);<br><br>       BaseContext.setCurrentId(empId);<span class="hljs-comment">//ThreadLocal</span><br>       filterChain.doFilter(request, response);<span class="hljs-comment">//放行请求</span><br>       <span class="hljs-keyword">return</span>;<br>   &#125;<br></code></pre></td></tr></table></figure>

<ul>
<li>这是将已登录员工的 id 封装在 ThreadLocal 中，为每一个使用到这个变量的线程创建了一个属于自己的副本</li>
<li>ThreadLocal 用作<strong>保存每个线程独享的对象</strong>，为每个线程都创建一个副本，这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本，确保<strong>线程安全</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">BaseContext</span> &#123;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ThreadLocal&lt;Long&gt; threadLocal = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ThreadLocal</span>();<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 设置当前线程的局部变量的值</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> id</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setCurrentId</span><span class="hljs-params">(Long id)</span> &#123;<br>        threadLocal.set(id);<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 返回当前线程对应的局部变量的值</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Long <span class="hljs-title function_">getCurrentId</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> threadLocal.get();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="公共字段自动填充"><a href="#公共字段自动填充" class="headerlink" title="公共字段自动填充"></a>公共字段自动填充</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> 邓哈哈</span><br><span class="hljs-comment"> * 2023/1/16 11:12</span><br><span class="hljs-comment"> * Function:</span><br><span class="hljs-comment"> * Version 1.0</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyMetaObjectHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">MetaObjectHandler</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 插入操作,自动填充</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> metaObject</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">insertFill</span><span class="hljs-params">(MetaObject metaObject)</span> &#123;<br>        log.info(<span class="hljs-string">&quot;公共字段自动填充...&quot;</span>);<br><br>        metaObject.setValue(<span class="hljs-string">&quot;createTime&quot;</span>, LocalDateTime.now());<br>        metaObject.setValue(<span class="hljs-string">&quot;updateTime&quot;</span>, LocalDateTime.now());<br>        metaObject.setValue(<span class="hljs-string">&quot;createUser&quot;</span>, BaseContext.getCurrentId());<br>        metaObject.setValue(<span class="hljs-string">&quot;updateUser&quot;</span>, BaseContext.getCurrentId());<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 更新操作,自动填充</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> metaObject</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">updateFill</span><span class="hljs-params">(MetaObject metaObject)</span> &#123;<br>        metaObject.setValue(<span class="hljs-string">&quot;updateTime&quot;</span>, LocalDateTime.now());<br>        metaObject.setValue(<span class="hljs-string">&quot;updateUser&quot;</span>, BaseContext.getCurrentId());<br>    &#125;<br>&#125;<br><br></code></pre></td></tr></table></figure>

<ul>
<li><p>这段代码是一个实现了 MyBatis 的 MetaObjectHandler 接口的类。它的主要作用是<strong>实现自动填充功能</strong>，用于在插入和更新操作时自动填充某些公共字段的值</p>
</li>
<li><p>具体来说，这段代码通过<strong>重写 insertFill 和 updateFill 方法实现了自动填充功能</strong>：</p>
<ul>
<li><p>insertFill 方法会在插入操作时被调用，自动填充一些字段的值。在这段代码中，它会设置 createTime、updateTime、createUser 和 updateUser 字段的值。其中，createTime 和 updateTime 字段会被设置为当前时间，createUser 和 updateUser 字段会被设置为当前用户的 ID</p>
</li>
<li><p>updateFill 方法会在更新操作时被调用，同样实现自动填充功能。在这段代码中，它会设置 updateTime 和 updateUser 字段的值，分别设置为当前时间和当前用户的 ID</p>
</li>
</ul>
</li>
<li><h5 id="通过使用这个自定义的-MyMetaObjectHandler-类，可以实现在插入和更新操作时自动填充公共字段的值，避免了手动在每次操作中设置这些字段的麻烦，提高了开发效率（2023-08-22-早）"><a href="#通过使用这个自定义的-MyMetaObjectHandler-类，可以实现在插入和更新操作时自动填充公共字段的值，避免了手动在每次操作中设置这些字段的麻烦，提高了开发效率（2023-08-22-早）" class="headerlink" title="通过使用这个自定义的 MyMetaObjectHandler 类，可以实现在插入和更新操作时自动填充公共字段的值，避免了手动在每次操作中设置这些字段的麻烦，提高了开发效率（2023/08/22 早）"></a>通过使用这个自定义的 MyMetaObjectHandler 类，可以实现在插入和更新操作时自动填充公共字段的值，避免了手动在每次操作中设置这些字段的麻烦，提高了开发效率（2023/08/22 早）</h5></li>
</ul>
<h3 id="Lombok"><a href="#Lombok" class="headerlink" title="Lombok"></a>Lombok</h3><h3 id="commons-lang3"><a href="#commons-lang3" class="headerlink" title="commons-lang3"></a>commons-lang3</h3><h3 id="Gson"><a href="#Gson" class="headerlink" title="Gson"></a>Gson</h3><ul>
<li><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844904154113146894?searchId=20230818150655BD9BB19565F023A9B0A5">求求你了，不要再自己实现这些逻辑了，开源工具类不香吗？ - 掘金 (juejin.cn)</a></li>
</ul>
<h3 id="基于基础类，开发一个-VO-类"><a href="#基于基础类，开发一个-VO-类" class="headerlink" title="基于基础类，开发一个 VO 类"></a>基于基础类，开发一个 VO 类</h3><ul>
<li>在开发过程，我们经常中使用基础类来开发 VO 类，是因为：<strong>（2023/09/14 晚）</strong></li>
</ul>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs">基础类可以作为扩展点，用于定义VO类的共同特性和行为。<br>你可以在基础类中定义一些通用的属性和方法，然后在VO类中添加自定义属性和方法，以实现更具体的功能<br></code></pre></td></tr></table></figure>

<ul>
<li><p>最近在开发 MemoryChat 项目的过程中，遇到了这样的业务问题：</p>
</li>
<li><p>查询用户<strong>已加入的队伍</strong> / <strong>已创建的队伍</strong>，我想要展示队伍的 username（队长昵称），在 team 表中只有 userId 字段（队长 Id）</p>
</li>
<li><p>这就需要多表联查，封装携带队伍信息以及队长 username 的对象，并返回给前端</p>
</li>
<li><h5 id="那我们就开发一个-teamVO-类，用-username-字段代替-userId-字段，作为封装返回的对象："><a href="#那我们就开发一个-teamVO-类，用-username-字段代替-userId-字段，作为封装返回的对象：" class="headerlink" title="那我们就开发一个 teamVO 类，用 username 字段代替 userId 字段，作为封装返回的对象："></a>那我们就开发一个 teamVO 类，用 username 字段代替 userId 字段，作为封装返回的对象：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> 邓哈哈</span><br><span class="hljs-comment"> * 2023/9/12 23:28</span><br><span class="hljs-comment"> * Function:</span><br><span class="hljs-comment"> * Version 1.0</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TeamVO</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * id</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@TableId(type = IdType.ASSIGN_ID)</span><br>    <span class="hljs-keyword">private</span> Long id;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 队伍名称</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String name;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 描述</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String description;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 最大人数</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> Integer maxNum;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 队长昵称</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String userName;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 队伍图片</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String imgUrl;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 已加人数</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> Integer joinNum;<br><br>............................<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="根据相关查询条件，正确执行业务逻辑获取到-teamList-后，转换为-teamVOList"><a href="#根据相关查询条件，正确执行业务逻辑获取到-teamList-后，转换为-teamVOList" class="headerlink" title="根据相关查询条件，正确执行业务逻辑获取到 teamList 后，转换为 teamVOList"></a>根据相关查询条件，正确执行业务逻辑获取到 teamList 后，转换为 teamVOList</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 转换teamList为teamVOList</span><br>List&lt;TeamVO&gt; teamVOList = getTeamVOByTeam(teamList);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">  * 转换teamList为teamVOList</span><br><span class="hljs-comment">  *</span><br><span class="hljs-comment">  * <span class="hljs-doctag">@param</span> teamList teamList</span><br><span class="hljs-comment">  * <span class="hljs-doctag">@return</span> teamVOList</span><br><span class="hljs-comment">  */</span><br> <span class="hljs-keyword">public</span> List&lt;TeamVO&gt; <span class="hljs-title function_">getTeamVOByTeam</span><span class="hljs-params">(List&lt;Team&gt; teamList)</span> &#123;<br>     List&lt;TeamVO&gt; teamVOList = teamList.stream().map(team -&gt; &#123;<br>         <span class="hljs-type">Long</span> <span class="hljs-variable">userId</span> <span class="hljs-operator">=</span> team.getUserId();<br>         <span class="hljs-type">String</span> <span class="hljs-variable">userName</span> <span class="hljs-operator">=</span> userService.getById(userId).getUsername();<br>         <span class="hljs-type">TeamVO</span> <span class="hljs-variable">teamVO</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">TeamVO</span>();<br><br>         teamVO.setId(team.getId());<br>         teamVO.setName(team.getName());<br>         teamVO.setDescription(team.getDescription());<br>         teamVO.setMaxNum(team.getMaxNum());<br>         teamVO.setUserName(userName);<br>         teamVO.setImgUrl(team.getImgUrl());<br>         teamVO.setJoinNum(team.getJoinNum());<br>         teamVO.setStatus(team.getStatus());<br>         teamVO.setExpireTime(team.getExpireTime());<br>         teamVO.setCreateTime(team.getCreateTime());<br>         teamVO.setUpdateTime(team.getUpdateTime());<br>         teamVO.setIsDelete(team.getIsDelete());<br><br>         <span class="hljs-keyword">return</span> teamVO;<br>     &#125;).collect(Collectors.toList());<br><br>     <span class="hljs-keyword">return</span> teamVOList;<br> &#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="就这样，我们将队伍信息和队长-username-封装在了-teamVOList-对象中，成功返回到了前端"><a href="#就这样，我们将队伍信息和队长-username-封装在了-teamVOList-对象中，成功返回到了前端" class="headerlink" title="就这样，我们将队伍信息和队长 username 封装在了 teamVOList 对象中，成功返回到了前端"></a>就这样，我们将队伍信息和队长 username 封装在了 teamVOList 对象中，成功返回到了前端</h5></li>
<li><p>现在，我们可以看到队伍的<strong>队长信息</strong>了：<strong>（2023/09/14 晚）</strong></p>
</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230914222548059.png" alt="image-20230914222548059"></p>
<h2 id="踩坑记录"><a href="#踩坑记录" class="headerlink" title="踩坑记录"></a>踩坑记录</h2><ul>
<li><h5 id="每当我兴致勃勃地启动项目，准备大展身手的时候，项目启动窗口总会弹出冰冷的报错信息"><a href="#每当我兴致勃勃地启动项目，准备大展身手的时候，项目启动窗口总会弹出冰冷的报错信息" class="headerlink" title="每当我兴致勃勃地启动项目，准备大展身手的时候，项目启动窗口总会弹出冰冷的报错信息"></a>每当我兴致勃勃地启动项目，准备大展身手的时候，项目启动窗口总会弹出冰冷的报错信息</h5></li>
<li><h5 id="这里列举了：我遇到过的所有启动项目报错的解决办法"><a href="#这里列举了：我遇到过的所有启动项目报错的解决办法" class="headerlink" title="这里列举了：我遇到过的所有启动项目报错的解决办法"></a>这里列举了：我遇到过的所有启动项目报错的解决办法</h5></li>
</ul>
<h3 id="XXXMapper-包扫描不到"><a href="#XXXMapper-包扫描不到" class="headerlink" title="XXXMapper 包扫描不到"></a>XXXMapper 包扫描不到</h3><ul>
<li><h5 id="当你看到这样的报错，你会怎么解决呢："><a href="#当你看到这样的报错，你会怎么解决呢：" class="headerlink" title="当你看到这样的报错，你会怎么解决呢："></a>当你看到这样的报错，你会怎么解决呢：</h5></li>
</ul>
<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs dart">Unsatisfied dependency expressed through field <span class="hljs-string">&#x27;baseMapper&#x27;</span>; nested exception <span class="hljs-keyword">is</span> org.springframework.beans.<span class="hljs-keyword">factory</span>.NoSuchBeanDefinitionException: No qualifying bean of type <span class="hljs-string">&#x27;com.memory.memoryiconbackend.mapper.WallpaperMapper&#x27;</span> available: expected at least <span class="hljs-number">1</span> bean which qualifies <span class="hljs-keyword">as</span> autowire candidate. Dependency annotations: &#123;<span class="hljs-meta">@org</span>.springframework.beans.<span class="hljs-keyword">factory</span>.annotation.Autowired(<span class="hljs-keyword">required</span>=<span class="hljs-keyword">true</span>)&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="这个报错信息大致意思是，未扫描到你的-XXXMapper-包，项目启动失败"><a href="#这个报错信息大致意思是，未扫描到你的-XXXMapper-包，项目启动失败" class="headerlink" title="这个报错信息大致意思是，未扫描到你的 XXXMapper 包，项目启动失败"></a>这个报错信息大致意思是，未扫描到你的 XXXMapper 包，项目启动失败</h5></li>
<li><h5 id="这个问题可谓最常见了，刚刚我就又被这个问题恶心到了，网上查了半天，感觉他们都是一知半解"><a href="#这个问题可谓最常见了，刚刚我就又被这个问题恶心到了，网上查了半天，感觉他们都是一知半解" class="headerlink" title="这个问题可谓最常见了，刚刚我就又被这个问题恶心到了，网上查了半天，感觉他们都是一知半解"></a>这个问题可谓最常见了，刚刚我就又被这个问题恶心到了，网上查了半天，感觉他们都是一知半解</h5></li>
<li><h5 id="那么我是怎么解决这个问题的呢？思路如下："><a href="#那么我是怎么解决这个问题的呢？思路如下：" class="headerlink" title="那么我是怎么解决这个问题的呢？思路如下："></a>那么我是怎么解决这个问题的呢？思路如下：</h5></li>
</ul>
<h4 id="XXXMapper-xml-配置错误"><a href="#XXXMapper-xml-配置错误" class="headerlink" title="XXXMapper.xml 配置错误"></a>XXXMapper.xml 配置错误</h4><ul>
<li><h5 id="检查-resource-下的-XXXMapper-xml-配置，检查实体类扫描和-mapper-扫描路径是否正确："><a href="#检查-resource-下的-XXXMapper-xml-配置，检查实体类扫描和-mapper-扫描路径是否正确：" class="headerlink" title="检查 resource 下的 XXXMapper.xml 配置，检查实体类扫描和 mapper 扫描路径是否正确："></a>检查 resource 下的 XXXMapper.xml 配置，检查实体类扫描和 mapper 扫描路径是否正确：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230801180028233.png" alt="image-20230801180028233"></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-meta">&lt;?xml version=<span class="hljs-string">&quot;1.0&quot;</span> encoding=<span class="hljs-string">&quot;UTF-8&quot;</span>?&gt;</span><br><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">mapper</span></span><br><span class="hljs-meta">        <span class="hljs-keyword">PUBLIC</span> <span class="hljs-string">&quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;</span></span><br><span class="hljs-meta">        <span class="hljs-string">&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">mapper</span> <span class="hljs-attr">namespace</span>=<span class="hljs-string">&quot;com.memory.memoryiconbackend.mapper.WallpaperMapper&quot;</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">resultMap</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;BaseResultMap&quot;</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;com.memory.memoryiconbackend.model.Wallpaper&quot;</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;id&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;id&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;name&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;name&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;url&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;url&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;type&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;type&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;tags&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;tags&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;createTime&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;create_time&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;updateTime&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;update_time&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;isDelete&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;is_delete&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">result</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;userId&quot;</span> <span class="hljs-attr">column</span>=<span class="hljs-string">&quot;user_id&quot;</span> <span class="hljs-attr">jdbcType</span>=<span class="hljs-string">&quot;VARCHAR&quot;</span>/&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">resultMap</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">sql</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;Base_Column_List&quot;</span>&gt;</span><br>        id,name,url,<br>        type,tags,create_time,<br>        update_time,is_delete,user_id<br>    <span class="hljs-tag">&lt;/<span class="hljs-name">sql</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">mapper</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="确保-XXXMapper-包的扫描路径正确后，再继续排查："><a href="#确保-XXXMapper-包的扫描路径正确后，再继续排查：" class="headerlink" title="确保 XXXMapper 包的扫描路径正确后，再继续排查："></a>确保 XXXMapper 包的扫描路径正确后，再继续排查：</h5></li>
</ul>
<h4 id="XXXMapper-上添加-Mapper-注解"><a href="#XXXMapper-上添加-Mapper-注解" class="headerlink" title="XXXMapper 上添加@Mapper 注解"></a>XXXMapper 上添加@Mapper 注解</h4><ul>
<li><h5 id="检查-XXXMapper-上是否添加了-Mapper-注解："><a href="#检查-XXXMapper-上是否添加了-Mapper-注解：" class="headerlink" title="检查 XXXMapper 上是否添加了@Mapper 注解："></a>检查 XXXMapper 上是否添加了@Mapper 注解：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230801180147709.png" alt="image-20230801180147709"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Mapper</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">WallpaperMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">BaseMapper</span>&lt;Wallpaper&gt; &#123;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="如果这两部还没有解决你的问题，请一定继续往下看："><a href="#如果这两部还没有解决你的问题，请一定继续往下看：" class="headerlink" title="如果这两部还没有解决你的问题，请一定继续往下看："></a>如果这两部还没有解决你的问题，请一定继续往下看：</h5></li>
</ul>
<h4 id="开启-MapperScan-注解"><a href="#开启-MapperScan-注解" class="headerlink" title="开启@MapperScan 注解"></a>开启@MapperScan 注解</h4><ul>
<li><h5 id="MapperScan-注解是干嘛的呢？它是用来在项目启动后，扫描你的-XXXMapper-所在路径，用法如下："><a href="#MapperScan-注解是干嘛的呢？它是用来在项目启动后，扫描你的-XXXMapper-所在路径，用法如下：" class="headerlink" title="@MapperScan 注解是干嘛的呢？它是用来在项目启动后，扫描你的 XXXMapper 所在路径，用法如下："></a>@MapperScan 注解是干嘛的呢？它是用来在项目启动后，扫描你的 XXXMapper 所在路径，用法如下：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><br><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-meta">@MapperScan(&quot;com.memory.memoryiconbackend.mapper.WallpaperMapper&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MemoryIconBackendApplication</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        SpringApplication.run(MemoryIconBackendApplication.class, args);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="那这个注解跟上面提到的-Mapper-注解，功能不是一样的吗？都是将-XXXMapper-标注为一个-Bean，交给-Spring-管理"><a href="#那这个注解跟上面提到的-Mapper-注解，功能不是一样的吗？都是将-XXXMapper-标注为一个-Bean，交给-Spring-管理" class="headerlink" title="那这个注解跟上面提到的@Mapper 注解，功能不是一样的吗？都是将 XXXMapper 标注为一个 Bean，交给 Spring 管理"></a>那这个注解跟上面提到的@Mapper 注解，功能不是一样的吗？都是将 XXXMapper 标注为一个 Bean，交给 Spring 管理</h5></li>
<li><h5 id="没错，这两个注解的作用是可以说是一摸一样的，无非就是写的位置不一样"><a href="#没错，这两个注解的作用是可以说是一摸一样的，无非就是写的位置不一样" class="headerlink" title="没错，这两个注解的作用是可以说是一摸一样的，无非就是写的位置不一样"></a>没错，这两个注解的作用是可以说是一摸一样的，无非就是写的位置不一样</h5></li>
<li><h5 id="正是因为这两个注解作用是一样的，所以在开发过程中，这两个注解写一个就行，而且只能写一个，否则会报错"><a href="#正是因为这两个注解作用是一样的，所以在开发过程中，这两个注解写一个就行，而且只能写一个，否则会报错" class="headerlink" title="正是因为这两个注解作用是一样的，所以在开发过程中，这两个注解写一个就行，而且只能写一个，否则会报错"></a>正是因为这两个注解作用是一样的，所以在开发过程中，这两个注解写一个就行，而且只能写一个，否则会报错</h5></li>
<li><h5 id="网上总会有蠢蛋，说在-XXXMapper-上，添加了-Mapper-注解之后，一定不要忘了在启动类上添加-MapperScan-注解"><a href="#网上总会有蠢蛋，说在-XXXMapper-上，添加了-Mapper-注解之后，一定不要忘了在启动类上添加-MapperScan-注解" class="headerlink" title="网上总会有蠢蛋，说在 XXXMapper 上，添加了@Mapper 注解之后，一定不要忘了在启动类上添加@MapperScan 注解"></a>网上总会有蠢蛋，说在 XXXMapper 上，添加了@Mapper 注解之后，一定不要忘了在启动类上添加@MapperScan 注解</h5></li>
<li><h5 id="这种方法肯定解决不了问题，是大错特错的"><a href="#这种方法肯定解决不了问题，是大错特错的" class="headerlink" title="这种方法肯定解决不了问题，是大错特错的"></a>这种方法肯定解决不了问题，是大错特错的</h5></li>
<li><h5 id="所以，如果你已经在-XXXMapper-上添加了-Mapper-注解，一定记得删除启动类上的-MapperScan-注解"><a href="#所以，如果你已经在-XXXMapper-上添加了-Mapper-注解，一定记得删除启动类上的-MapperScan-注解" class="headerlink" title="所以，如果你已经在 XXXMapper 上添加了@Mapper 注解，一定记得删除启动类上的@MapperScan 注解"></a>所以，如果你已经在 XXXMapper 上添加了@Mapper 注解，一定记得删除启动类上的@MapperScan 注解</h5></li>
<li><h5 id="如果到这里，你已经按照上面的方法解决了问题，成功启动了项目，恭喜你；如果仍旧报错，请继续往下看："><a href="#如果到这里，你已经按照上面的方法解决了问题，成功启动了项目，恭喜你；如果仍旧报错，请继续往下看：" class="headerlink" title="如果到这里，你已经按照上面的方法解决了问题，成功启动了项目，恭喜你；如果仍旧报错，请继续往下看："></a>如果到这里，你已经按照上面的方法解决了问题，成功启动了项目，恭喜你；如果仍旧报错，请继续往下看：</h5></li>
</ul>
<h4 id="MybatisPlusConfig-配置"><a href="#MybatisPlusConfig-配置" class="headerlink" title="MybatisPlusConfig 配置"></a>MybatisPlusConfig 配置</h4><ul>
<li><h5 id="我们在项目中，导入了-MybatisPlus-依赖之后，总会写一个-MybatisPlusConfig-的分页配置："><a href="#我们在项目中，导入了-MybatisPlus-依赖之后，总会写一个-MybatisPlusConfig-的分页配置：" class="headerlink" title="我们在项目中，导入了 MybatisPlus 依赖之后，总会写一个 MybatisPlusConfig 的分页配置："></a>我们在项目中，导入了 MybatisPlus 依赖之后，总会写一个 MybatisPlusConfig 的分页配置：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * mybatis-plus 分页配置类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@MapperScan(&quot;com.memory.memoryiconbackend.mapper.WallpaperMapper&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MybatisPlusConfig</span> &#123;<br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> MybatisPlusInterceptor <span class="hljs-title function_">mybatisPlusInterceptor</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-type">MybatisPlusInterceptor</span> <span class="hljs-variable">interceptor</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MybatisPlusInterceptor</span>();<br>        interceptor.addInnerInterceptor(<span class="hljs-keyword">new</span> <span class="hljs-title class_">PaginationInnerInterceptor</span>(DbType.MYSQL));<br>        <span class="hljs-keyword">return</span> interceptor;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="如果你的问题没有解决，一定是因为在这个配置类上边，写上了-MapperScan-注解："><a href="#如果你的问题没有解决，一定是因为在这个配置类上边，写上了-MapperScan-注解：" class="headerlink" title="如果你的问题没有解决，一定是因为在这个配置类上边，写上了@MapperScan 注解："></a>如果你的问题没有解决，一定是因为在这个配置类上边，写上了@MapperScan 注解：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230801182811187.png" alt="image-20230801182811187"></p>
<ul>
<li><h5 id="而这个注解的作用，跟启动类上的-MapperScan-注解的作用是一模一样的，删除它就好了"><a href="#而这个注解的作用，跟启动类上的-MapperScan-注解的作用是一模一样的，删除它就好了" class="headerlink" title="而这个注解的作用，跟启动类上的@MapperScan 注解的作用是一模一样的，删除它就好了"></a>而这个注解的作用，跟启动类上的@MapperScan 注解的作用是一模一样的，删除它就好了</h5></li>
</ul>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><ul>
<li><h5 id="如果你已经在-XXXMapper-上添加了-Mapper-注解，请把启动类和-MybatisPlusConfig-配置类上的-MapperScan-注解删除"><a href="#如果你已经在-XXXMapper-上添加了-Mapper-注解，请把启动类和-MybatisPlusConfig-配置类上的-MapperScan-注解删除" class="headerlink" title="如果你已经在 XXXMapper 上添加了@Mapper 注解，请把启动类和 MybatisPlusConfig 配置类上的@MapperScan 注解删除"></a>如果你已经在 XXXMapper 上添加了@Mapper 注解，请把启动类和 MybatisPlusConfig 配置类上的@MapperScan 注解删除</h5></li>
<li><h5 id="如果你已经在启动类和-MybatisPlusConfig-配置类上添加了-MapperScan-注解，请把-XXXMapper-上的-Mapper-注解删除"><a href="#如果你已经在启动类和-MybatisPlusConfig-配置类上添加了-MapperScan-注解，请把-XXXMapper-上的-Mapper-注解删除" class="headerlink" title="如果你已经在启动类和 MybatisPlusConfig 配置类上添加了@MapperScan 注解，请把 XXXMapper 上的@Mapper 注解删除"></a>如果你已经在启动类和 MybatisPlusConfig 配置类上添加了@MapperScan 注解，请把 XXXMapper 上的@Mapper 注解删除</h5></li>
<li><h5 id="希望这篇文章对你有帮助，感谢您的支持！😁"><a href="#希望这篇文章对你有帮助，感谢您的支持！😁" class="headerlink" title="希望这篇文章对你有帮助，感谢您的支持！😁"></a>希望这篇文章对你有帮助，感谢您的支持！😁</h5></li>
</ul>
<h3 id="类文件版本不匹配"><a href="#类文件版本不匹配" class="headerlink" title="类文件版本不匹配"></a>类文件版本不匹配</h3><ul>
<li><h5 id="如果在启动项目时，出现了这样的报错，你会怎么解决呢："><a href="#如果在启动项目时，出现了这样的报错，你会怎么解决呢：" class="headerlink" title="如果在启动项目时，出现了这样的报错，你会怎么解决呢："></a>如果在启动项目时，出现了这样的报错，你会怎么解决呢：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230802082050802.png" alt="image-20230802082050802"></p>
<ul>
<li><h5 id="引起这样报错的原因只有一种：类文件版本不匹配，即项目里导入的依赖版本不兼容"><a href="#引起这样报错的原因只有一种：类文件版本不匹配，即项目里导入的依赖版本不兼容" class="headerlink" title="引起这样报错的原因只有一种：类文件版本不匹配，即项目里导入的依赖版本不兼容"></a>引起这样报错的原因只有一种：类文件版本不匹配，即项目里导入的依赖版本不兼容</h5></li>
</ul>
<h4 id="Mybatis-版本-和-SpringBoot-版本不兼容"><a href="#Mybatis-版本-和-SpringBoot-版本不兼容" class="headerlink" title="Mybatis 版本 和 SpringBoot 版本不兼容"></a>Mybatis 版本 和 SpringBoot 版本不兼容</h4><ul>
<li><h5 id="查看上方报错信息，很显然是-XXXMapper-包扫描失败了，这就是-Mybatis-版本-和-SpringBoot-版本不兼容的问题"><a href="#查看上方报错信息，很显然是-XXXMapper-包扫描失败了，这就是-Mybatis-版本-和-SpringBoot-版本不兼容的问题" class="headerlink" title="查看上方报错信息，很显然是 XXXMapper 包扫描失败了，这就是 Mybatis 版本 和 SpringBoot 版本不兼容的问题"></a>查看上方报错信息，很显然是 XXXMapper 包扫描失败了，这就是 Mybatis 版本 和 SpringBoot 版本不兼容的问题</h5></li>
<li><h5 id="如此配置：-2023-08-02-早"><a href="#如此配置：-2023-08-02-早" class="headerlink" title="如此配置：(2023/08/02 早)"></a>如此配置：(2023/08/02 早)</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.3.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>      <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.7.14<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span><br></code></pre></td></tr></table></figure>

<h4 id="Jdk-版本-和-SpringBoot-版本不兼容"><a href="#Jdk-版本-和-SpringBoot-版本不兼容" class="headerlink" title="Jdk 版本 和 SpringBoot 版本不兼容"></a>Jdk 版本 和 SpringBoot 版本不兼容</h4><ul>
<li><h5 id="当你启动项目时，看到了这样的报错："><a href="#当你启动项目时，看到了这样的报错：" class="headerlink" title="当你启动项目时，看到了这样的报错："></a>当你启动项目时，看到了这样的报错：</h5></li>
</ul>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230802182601591.png" alt="image-20230802182601591"></p>
<ul>
<li><h5 id="这是由于-Jdk-版本-和-SpringBoot-版本不兼容导致的"><a href="#这是由于-Jdk-版本-和-SpringBoot-版本不兼容导致的" class="headerlink" title="这是由于 Jdk 版本 和 SpringBoot 版本不兼容导致的"></a>这是由于 Jdk 版本 和 SpringBoot 版本不兼容导致的</h5></li>
<li><h5 id="JDK-版本不兼容，还并且可能导致其他配置构建失败："><a href="#JDK-版本不兼容，还并且可能导致其他配置构建失败：" class="headerlink" title="JDK 版本不兼容，还并且可能导致其他配置构建失败："></a>JDK 版本不兼容，还并且可能导致其他配置构建失败：</h5></li>
</ul>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230802182701312.png" alt="image-20230802182701312"></p>
<ul>
<li><h5 id="这是我的-SpringBoot-坐标依赖："><a href="#这是我的-SpringBoot-坐标依赖：" class="headerlink" title="这是我的 SpringBoot 坐标依赖："></a>这是我的 SpringBoot 坐标依赖：</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.7.9<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span><br>   <span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="按照提示，升级-JDK-版本到-1-8-以上即可解决问题"><a href="#按照提示，升级-JDK-版本到-1-8-以上即可解决问题" class="headerlink" title="按照提示，升级 JDK 版本到 1.8 以上即可解决问题"></a>按照提示，升级 JDK 版本到 1.8 以上即可解决问题</h5></li>
</ul>
<h3 id="相关依赖未导入"><a href="#相关依赖未导入" class="headerlink" title="相关依赖未导入"></a>相关依赖未导入</h3><h4 id="MySQL-相关依赖未导入"><a href="#MySQL-相关依赖未导入" class="headerlink" title="MySQL 相关依赖未导入"></a>MySQL 相关依赖未导入</h4><ul>
<li><h5 id="今天在做-PicMemories-项目过程中，启动项目时，出现了以下报错："><a href="#今天在做-PicMemories-项目过程中，启动项目时，出现了以下报错：" class="headerlink" title="今天在做 PicMemories 项目过程中，启动项目时，出现了以下报错："></a>今天在做 PicMemories 项目过程中，启动项目时，出现了以下报错：</h5></li>
</ul>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean <span class="hljs-keyword">with</span> <span class="hljs-type">name</span> <span class="hljs-string">&#x27;downloadServiceImpl&#x27;</span>: Unsatisfied dependency expressed through field <span class="hljs-string">&#x27;baseMapper&#x27;</span>; nested <span class="hljs-keyword">exception</span> <span class="hljs-keyword">is</span> org.springframework.beans.factory.BeanCreationException: Error creating bean <span class="hljs-keyword">with</span> <span class="hljs-type">name</span> <span class="hljs-string">&#x27;downloadMapper&#x27;</span> defined <span class="hljs-keyword">in</span> file [D:\Project\星球项目\PicMemories\target\classes\com\memory\picmemories\mapper\DownloadMapper.<span class="hljs-keyword">class</span>]: Cannot resolve reference <span class="hljs-keyword">to</span> bean <span class="hljs-string">&#x27;sqlSessionTemplate&#x27;</span> <span class="hljs-keyword">while</span> setting bean property <span class="hljs-string">&#x27;sqlSessionTemplate&#x27;</span>; nested <span class="hljs-keyword">exception</span> <span class="hljs-keyword">is</span> org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean <span class="hljs-keyword">with</span> <span class="hljs-type">name</span> <span class="hljs-string">&#x27;dataSourceScriptDatabaseInitializer&#x27;</span> defined <span class="hljs-keyword">in</span> <span class="hljs-keyword">class</span> <span class="hljs-type">path</span> resource [org/springframework/boot/autoconfigure/<span class="hljs-keyword">sql</span>/init/DataSourceInitializationConfiguration.<span class="hljs-keyword">class</span>]: Unsatisfied dependency expressed through <span class="hljs-keyword">method</span> <span class="hljs-string">&#x27;dataSourceScriptDatabaseInitializer&#x27;</span> parameter <span class="hljs-number">0</span>; nested <span class="hljs-keyword">exception</span> <span class="hljs-keyword">is</span> org.springframework.beans.factory.BeanCreationException: Error creating bean <span class="hljs-keyword">with</span> <span class="hljs-type">name</span> <span class="hljs-string">&#x27;dataSource&#x27;</span> defined <span class="hljs-keyword">in</span> <span class="hljs-keyword">class</span> <span class="hljs-type">path</span> resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.<span class="hljs-keyword">class</span>]: Bean instantiation via factory <span class="hljs-keyword">method</span> failed; nested <span class="hljs-keyword">exception</span> <span class="hljs-keyword">is</span> org.springframework.beans.BeanInstantiationException: Failed <span class="hljs-keyword">to</span> instantiate [com.zaxxer.hikari.HikariDataSource]: Factory <span class="hljs-keyword">method</span> <span class="hljs-string">&#x27;dataSource&#x27;</span> threw <span class="hljs-keyword">exception</span>; nested <span class="hljs-keyword">exception</span> <span class="hljs-keyword">is</span> java.lang.IllegalStateException: Cannot <span class="hljs-keyword">load</span> driver <span class="hljs-keyword">class</span>: com.mysql.cj.jdbc.Driver<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="经排查，发现是-MySQL-相关依赖导入错了，我导入的错误依赖如下："><a href="#经排查，发现是-MySQL-相关依赖导入错了，我导入的错误依赖如下：" class="headerlink" title="经排查，发现是 MySQL 相关依赖导入错了，我导入的错误依赖如下："></a>经排查，发现是 MySQL 相关依赖导入错了，我导入的错误依赖如下：</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.microsoft.sqlserver<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mssql-jdbc<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>runtime<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="正确的依赖应该为："><a href="#正确的依赖应该为：" class="headerlink" title="正确的依赖应该为："></a>正确的依赖应该为：</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-j<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>runtime<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span><br>   <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="为什么会出现这样的错误呢？是因为我在构建项目时，选择导入以下依赖中，发生了错误："><a href="#为什么会出现这样的错误呢？是因为我在构建项目时，选择导入以下依赖中，发生了错误：" class="headerlink" title="为什么会出现这样的错误呢？是因为我在构建项目时，选择导入以下依赖中，发生了错误："></a>为什么会出现这样的错误呢？是因为我在构建项目时，选择导入以下依赖中，发生了错误：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230802183420381.png" alt="image-20230802183420381"></p>
<ul>
<li><h5 id="这里一定要选择-MySQL-Driver，而不是-MySQL-Server-Driver，否则就会导入错误的依赖而引发报错（2023-08-02-午）"><a href="#这里一定要选择-MySQL-Driver，而不是-MySQL-Server-Driver，否则就会导入错误的依赖而引发报错（2023-08-02-午）" class="headerlink" title="这里一定要选择 MySQL Driver，而不是 MySQL Server Driver，否则就会导入错误的依赖而引发报错（2023/08/02 午）"></a>这里一定要选择 MySQL Driver，而不是 MySQL Server Driver，否则就会导入错误的依赖而引发报错（2023/08/02 午）</h5></li>
</ul>
<h3 id="快速生成-Swagger-Knif4j-接口文档"><a href="#快速生成-Swagger-Knif4j-接口文档" class="headerlink" title="快速生成 Swagger + Knif4j 接口文档"></a>快速生成 <code>Swagger</code> + <code>Knif4j</code> 接口文档</h3><ul>
<li><p>昨天遇到的问题，今天总算解决了，废话少说，上案例：</p>
</li>
<li><p>在开发 Memory-icon 和 PicMemories 项目时，都遇到了这个问题：明明 Knif4j 配置无误，接口文档访问却报 404 错误：</p>
</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230802184955677.png" alt="image-20230802184955677"></p>
<ul>
<li><h5 id="Knif4j-官方文档：快速开始-Knife4j-xiaominfo-com"><a href="#Knif4j-官方文档：快速开始-Knife4j-xiaominfo-com" class="headerlink" title="Knif4j 官方文档：快速开始 | Knife4j (xiaominfo.com)"></a>Knif4j 官方文档：<a target="_blank" rel="noopener" href="https://doc.xiaominfo.com/docs/quick-start">快速开始 | Knife4j (xiaominfo.com)</a></h5></li>
<li><p>按照官方文档，我们可以清楚地看到不同版本的 SpringBoot 导入 Swagger + Knif4j 接口文档的方式是不一样的</p>
</li>
<li><p><code>Spring Boot</code> 版本在 <code>2.4.0 </code>~ <code>3.0.0</code>之间，以我为例，我的 Spring Boot 版本是 2.7.9</p>
</li>
<li><p>那么使用 Swagger + Kni4j 自动生成接口文档的步骤如下：</p>
<ul>
<li><h5 id="导入依赖坐标"><a href="#导入依赖坐标" class="headerlink" title="导入依赖坐标"></a>导入依赖坐标</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.github.xiaoymin<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>knife4j-openapi2-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>      <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="在-appilcation-yaml-中导入配置："><a href="#在-appilcation-yaml-中导入配置：" class="headerlink" title="在 appilcation.yaml 中导入配置："></a>在 appilcation.yaml 中导入配置：</h5></li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">knife4j:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">openapi:</span><br>    <span class="hljs-attr">title:</span> <span class="hljs-string">PicMemories</span> <span class="hljs-string">接口文档</span><br>    <span class="hljs-attr">description:</span> <span class="hljs-string">PicMemories</span> <span class="hljs-string">壁纸分享小程序</span><br>    <span class="hljs-attr">concat:</span> <span class="hljs-number">3348407547</span><span class="hljs-string">@qq.com</span><br>    <span class="hljs-attr">url:</span> <span class="hljs-string">https://deng-2022.gitee.io/blog/</span><br>    <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><br>    <span class="hljs-attr">license:</span> <span class="hljs-string">Apache</span> <span class="hljs-number">2.0</span><br>    <span class="hljs-attr">group:</span><br>      <span class="hljs-attr">test1:</span><br>        <span class="hljs-attr">group-name:</span> <span class="hljs-string">壁纸分享</span><br></code></pre></td></tr></table></figure></li>
<li><h5 id="完毕，已经能够正常访问到接口文档了：http-localhost-8084-api-doc-html（2023-08-02-晚）"><a href="#完毕，已经能够正常访问到接口文档了：http-localhost-8084-api-doc-html（2023-08-02-晚）" class="headerlink" title="完毕，已经能够正常访问到接口文档了：http://localhost:8084/api/doc.html（2023/08/02 晚）"></a>完毕，已经能够正常访问到接口文档了：<a href="">http://localhost:8084/api/doc.html</a>（2023/08/02 晚）</h5></li>
</ul>
<h3 id="执行-SQL-找不到字段"><a href="#执行-SQL-找不到字段" class="headerlink" title="执行 SQL 找不到字段"></a>执行 SQL 找不到字段</h3><ul>
<li><h5 id="如果在执行-SQL-时，出现了以下问题："><a href="#如果在执行-SQL-时，出现了以下问题：" class="headerlink" title="如果在执行 SQL 时，出现了以下问题："></a>如果在执行 SQL 时，出现了以下问题：</h5></li>
</ul>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-literal">Unknown</span> <span class="hljs-keyword">column</span> <span class="hljs-string">&#x27;user_id&#x27;</span> <span class="hljs-keyword">in</span> <span class="hljs-string">&#x27;field list&#x27;</span><br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20230803082954240.png" alt="image-20230803082954240"></p>
<ul>
<li><h5 id="报错信息显示，找不到字段-user-id，解决思路如下："><a href="#报错信息显示，找不到字段-user-id，解决思路如下：" class="headerlink" title="报错信息显示，找不到字段 user_id，解决思路如下："></a>报错信息显示，找不到字段 user_id，解决思路如下：</h5><ul>
<li>首先，确定这个对应数据库中有这个字段 user_id</li>
<li>其次，确保实体类属性和表中字段映射无误</li>
<li>最后，检查数据库配置是否正确</li>
</ul>
</li>
<li><h5 id="我就栽在最后一步上了，数据库连接到别的表了，怪不得找不到相应字段，折磨了我一晚上（2023-08-03-早）"><a href="#我就栽在最后一步上了，数据库连接到别的表了，怪不得找不到相应字段，折磨了我一晚上（2023-08-03-早）" class="headerlink" title="我就栽在最后一步上了，数据库连接到别的表了，怪不得找不到相应字段，折磨了我一晚上（2023/08/03 早）"></a>我就栽在最后一步上了，数据库连接到别的表了，怪不得找不到相应字段，折磨了我一晚上（2023/08/03 早）</h5></li>
</ul>
<blockquote>
<p>2024 年 8 月 31 日</p>
</blockquote>
<p>栽他妈一晚上跟头</p>
<p><img src="http://blog.memory-life.xyz/image-20240831193750916.png" alt="image-20240831193750916"></p>
<h3 id="表记录-实体类-ID-自增长"><a href="#表记录-实体类-ID-自增长" class="headerlink" title="表记录/实体类 ID 自增长"></a>表记录/实体类 ID 自增长</h3><ul>
<li><h5 id="如何实现：往数据库表中插入数据记录时，id-自增长？"><a href="#如何实现：往数据库表中插入数据记录时，id-自增长？" class="headerlink" title="如何实现：往数据库表中插入数据记录时，id 自增长？"></a>如何实现：往数据库表中插入数据记录时，id 自增长？</h5><ul>
<li><h5 id="建表语句："><a href="#建表语句：" class="headerlink" title="建表语句："></a>建表语句：</h5></li>
</ul>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql">`user_id`   <span class="hljs-type">bigint</span> auto_increment <span class="hljs-keyword">primary</span> key comment <span class="hljs-string">&#x27;用户id&#x27;</span>,<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="实体类映射："><a href="#实体类映射：" class="headerlink" title="实体类映射："></a>实体类映射：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">  * 用户id</span><br><span class="hljs-comment">  */</span><br> <span class="hljs-meta">@TableId(type = IdType.ASSIGN_ID)</span><br> <span class="hljs-keyword">private</span> Long userId;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="插入数据："><a href="#插入数据：" class="headerlink" title="插入数据："></a>插入数据：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 3.向数据库中插入用户数据</span><br>       <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>();<br>       user.setUsername(username);<br>       user.setPassword(encryptPassword);<br>       user.setPassword(phone);<br>       user.setAvatar(<span class="hljs-string">&quot;http://ry2s7czdf.hd-bkt.clouddn.com/imgs/avatar/winter_nature_6-wallpaper-2560x1600.jpg&quot;</span>);<br>  <br>       <span class="hljs-type">boolean</span> <span class="hljs-variable">save</span> <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.save(user);<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="表记录：（2023-08-04-早）"><a href="#表记录：（2023-08-04-早）" class="headerlink" title="表记录：（2023/08/04 早）"></a>表记录：（2023/08/04 早）</h5></li>
</ul>
<figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf"><span class="hljs-attribute">id</span> <span class="hljs-operator">=</span> <span class="hljs-number">1687297781782978562</span><br></code></pre></td></tr></table></figure></li>
</ul>
<h3 id="测试类添加-SpringBootTest-注解"><a href="#测试类添加-SpringBootTest-注解" class="headerlink" title="测试类添加@SpringBootTest 注解"></a>测试类添加@SpringBootTest 注解</h3><ul>
<li><h5 id="如果不加这个注解，导入的对象值为-null：（2023-08-05-午）"><a href="#如果不加这个注解，导入的对象值为-null：（2023-08-05-午）" class="headerlink" title="如果不加这个注解，导入的对象值为 null：（2023/08/05 午）"></a>如果不加这个注解，导入的对象值为 null：（2023/08/05 午）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">class</span> <span class="hljs-title class_">MemoryClientTest</span> &#123;<br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> MemoryClient memoryClient;<br><br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testMemorySdk</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">if</span>(memoryClient != <span class="hljs-literal">null</span>)&#123;<br>            System.out.println(<span class="hljs-string">&quot;成功了&quot;</span>);<br>            memoryClient.getNameByGet(<span class="hljs-string">&quot;邓哈哈&quot;</span>);<br>            memoryClient.getNameByPost(<span class="hljs-string">&quot;邓嘻嘻&quot;</span>);<br><br><span class="hljs-comment">//        User user = new User();</span><br><span class="hljs-comment">//        user.setName(&quot;邓尼玛&quot;);</span><br>            <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>(<span class="hljs-string">&quot;邓尼玛&quot;</span>);<br>            memoryClient.getUserByPost(user);<br>        &#125;<span class="hljs-keyword">else</span> &#123;<br>            System.out.println(<span class="hljs-string">&quot;失败！&quot;</span>);<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="还有一点要注意，测试类返回值必须为-void，返回其他值会报错：（2023-08-07-早）"><a href="#还有一点要注意，测试类返回值必须为-void，返回其他值会报错：（2023-08-07-早）" class="headerlink" title="还有一点要注意，测试类返回值必须为 void，返回其他值会报错：（2023/08/07 早）"></a>还有一点要注意，测试类返回值必须为 void，返回其他值会报错：（2023/08/07 早）</h5></li>
</ul>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pgsql"><span class="hljs-keyword">no</span> test were <span class="hljs-built_in">found</span><br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20230807102218377.png" alt="image-20230807102218377"></p>
<h3 id="Enum-类不能使用-DATA-注解"><a href="#Enum-类不能使用-DATA-注解" class="headerlink" title="Enum 类不能使用@DATA 注解"></a>Enum 类不能使用@DATA 注解</h3><ul>
<li><h5 id="手写-getXXX-setXXX-："><a href="#手写-getXXX-setXXX-：" class="headerlink" title="手写 getXXX() / setXXX()："></a>手写 getXXX() / setXXX()：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">WallpaperStatusEnum</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 0 - 公开, 在队伍大厅中可以直接加入</span><br><span class="hljs-comment">     */</span><br>    REVIEW(<span class="hljs-number">0</span>, <span class="hljs-string">&quot;审核中&quot;</span>),<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 1 - 私有, 在队伍大厅中不可以直接加入</span><br><span class="hljs-comment">     */</span><br>    PASS(<span class="hljs-number">1</span>, <span class="hljs-string">&quot;已发布&quot;</span>),<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 2 - 公开且加密, 加入队伍需要密码</span><br><span class="hljs-comment">     */</span><br>    NOPASS(<span class="hljs-number">2</span>, <span class="hljs-string">&quot;不通过&quot;</span>);<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 状态码</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-type">int</span> value;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 状态描述</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String text;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 判断用户状态</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> value 用户状态</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 存在与否</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> WallpaperStatusEnum <span class="hljs-title function_">getEnumByValue</span><span class="hljs-params">(Integer value)</span> &#123;<br>        <span class="hljs-keyword">if</span> (value == <span class="hljs-literal">null</span>) &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>        &#125;<br>        WallpaperStatusEnum[] values = WallpaperStatusEnum.values();<br>        <span class="hljs-keyword">for</span> (WallpaperStatusEnum teamStatusEnum : values) &#123;<br>            <span class="hljs-keyword">if</span> (teamStatusEnum.getValue() == value) &#123;<br>                <span class="hljs-keyword">return</span> teamStatusEnum;<br>            &#125;<br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>    &#125;<br><br>    WallpaperStatusEnum(<span class="hljs-type">int</span> value, String text) &#123;<br>        <span class="hljs-built_in">this</span>.value = value;<br>        <span class="hljs-built_in">this</span>.text = text;<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">getValue</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> value;<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setValue</span><span class="hljs-params">(<span class="hljs-type">int</span> value)</span> &#123;<br>        <span class="hljs-built_in">this</span>.value = value;<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getText</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> text;<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setText</span><span class="hljs-params">(String text)</span> &#123;<br>        <span class="hljs-built_in">this</span>.text = text;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="循环依赖"><a href="#循环依赖" class="headerlink" title="循环依赖"></a>循环依赖</h3><ul>
<li><h5 id="我在项目中如此写："><a href="#我在项目中如此写：" class="headerlink" title="我在项目中如此写："></a>我在项目中如此写：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WallpaperServiceImpl</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ServiceImpl</span>&lt;WallpaperMapper, Wallpaper&gt;<br>        <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WallpaperService</span> &#123;<br>         <span class="hljs-meta">@Resource</span><br>         <span class="hljs-keyword">private</span> WallpaperService wallpaperService;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 分页查询</span><br><span class="hljs-comment">     * 分类查询壁纸</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 分类壁纸</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> List&lt;Wallpaper&gt; <span class="hljs-title function_">getPageByType</span><span class="hljs-params">(Integer searchType)</span> &#123;<br>        <span class="hljs-keyword">if</span> (WallpaperTypeEnum.getEnumByValue(searchType) == <span class="hljs-literal">null</span>) &#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BusinessException</span>(ErrorCode.PARMS_ERROR, <span class="hljs-string">&quot;没有这样的壁纸类型&quot;</span>);<br>        &#125;<br><br>        QueryWrapper&lt;Wallpaper&gt; type_wqw = <span class="hljs-keyword">new</span> <span class="hljs-title class_">QueryWrapper</span>&lt;&gt;();<br>        type_wqw.eq(<span class="hljs-string">&quot;type&quot;</span>, searchType);<br><br>        <span class="hljs-keyword">return</span> wallpaperService.list(type_wqw);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="产生了循环依赖，会报错："><a href="#产生了循环依赖，会报错：" class="headerlink" title="产生了循环依赖，会报错："></a>产生了循环依赖，会报错：</h5></li>
</ul>
<figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs livecodeserver">Description:<br><br>The dependencies <span class="hljs-keyword">of</span> some <span class="hljs-keyword">of</span> <span class="hljs-keyword">the</span> beans <span class="hljs-keyword">in</span> <span class="hljs-keyword">the</span> application context form <span class="hljs-keyword">a</span> cycle:<br><br>   wallpaperController<br>┌─────┐<br>|  wallpaperServiceImpl<br>└─────┘<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="应该写成这样："><a href="#应该写成这样：" class="headerlink" title="应该写成这样："></a>应该写成这样：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WallpaperServiceImpl</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ServiceImpl</span>&lt;WallpaperMapper, Wallpaper&gt;<br>        <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WallpaperService</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 分页查询</span><br><span class="hljs-comment">     * 分类查询壁纸</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 分类壁纸</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> List&lt;Wallpaper&gt; <span class="hljs-title function_">getPageByType</span><span class="hljs-params">(Integer searchType)</span> &#123;<br>        <span class="hljs-keyword">if</span> (WallpaperTypeEnum.getEnumByValue(searchType) == <span class="hljs-literal">null</span>) &#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BusinessException</span>(ErrorCode.PARMS_ERROR, <span class="hljs-string">&quot;没有这样的壁纸类型&quot;</span>);<br>        &#125;<br><br>        QueryWrapper&lt;Wallpaper&gt; type_wqw = <span class="hljs-keyword">new</span> <span class="hljs-title class_">QueryWrapper</span>&lt;&gt;();<br>        type_wqw.eq(<span class="hljs-string">&quot;type&quot;</span>, searchType);<br><br>        <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.list(type_wqw);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="时间戳格式问题"><a href="#时间戳格式问题" class="headerlink" title="时间戳格式问题"></a>时间戳格式问题</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@JsonFormat(pattern=&quot;yyyy-MM-dd HH:mm:ss&quot;,timezone = &quot;GMT+8&quot;)</span><br></code></pre></td></tr></table></figure>

<h3 id="MyBatis-逻辑删除"><a href="#MyBatis-逻辑删除" class="headerlink" title="MyBatis 逻辑删除"></a>MyBatis 逻辑删除</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 是否删除</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@TableLogic</span><br><span class="hljs-keyword">private</span> Integer isDelete;<br></code></pre></td></tr></table></figure>

<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">mybatis-plus:</span><br>  <span class="hljs-attr">global-config:</span><br>    <span class="hljs-comment"># 逻辑删除</span><br>    <span class="hljs-attr">db-config:</span><br>      <span class="hljs-attr">logic-delete-field:</span> <span class="hljs-string">isDelete</span> <span class="hljs-comment"># 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)</span><br>      <span class="hljs-attr">logic-delete-value:</span> <span class="hljs-number">1</span> <span class="hljs-comment"># 逻辑已删除值(默认为 1)</span><br>      <span class="hljs-attr">logic-not-delete-value:</span> <span class="hljs-number">0</span> <span class="hljs-comment"># 逻辑未删除值(默认为 0)</span><br></code></pre></td></tr></table></figure>

<h3 id="Redis-的引入和测试"><a href="#Redis-的引入和测试" class="headerlink" title="Redis 的引入和测试"></a>Redis 的引入和测试</h3><ul>
<li><h5 id="快速实现-Redis-的引入，主要做到以下几点：（2023-08-07-早）"><a href="#快速实现-Redis-的引入，主要做到以下几点：（2023-08-07-早）" class="headerlink" title="快速实现 Redis 的引入，主要做到以下几点：（2023/08/07 早）"></a>快速实现 Redis 的引入，主要做到以下几点：（2023/08/07 早）</h5><ul>
<li>导入相关依赖坐标</li>
<li>作相关 yaml 配置</li>
<li>作测试</li>
<li>项目引入</li>
</ul>
</li>
<li><h5 id="导入依赖坐标："><a href="#导入依赖坐标：" class="headerlink" title="导入依赖坐标："></a>导入依赖坐标：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;!-- https:<span class="hljs-comment">//mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --&gt;</span><br>     &lt;dependency&gt;<br>         &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>         &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;<br>         &lt;version&gt;<span class="hljs-number">2.6</span><span class="hljs-number">.4</span>&lt;/version&gt;<br>     &lt;/dependency&gt;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="写配置："><a href="#写配置：" class="headerlink" title="写配置："></a>写配置：</h5></li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># redis 配置</span><br><span class="hljs-attr">redis:</span><br>	<span class="hljs-attr">port:</span> <span class="hljs-number">6379</span><br>	<span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span><br>	<span class="hljs-attr">database:</span> <span class="hljs-number">0</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="作测试："><a href="#作测试：" class="headerlink" title="作测试："></a>作测试：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootTest</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">UserServiceImplTest</span> &#123;<br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> RedisTemplate redisTemplate;<br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-type">StringRedisTemplate</span> <span class="hljs-variable">stringRedisTemplate</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringRedisTemplate</span>();<br><br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-keyword">void</span> <span class="hljs-title function_">loginTest</span><span class="hljs-params">()</span> &#123;<br><br>        <span class="hljs-type">String</span> <span class="hljs-variable">redisKey</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;pic_memories:user:login:session_key&quot;</span>;<br><br><br>        stringRedisTemplate.opsForValue().set(<span class="hljs-string">&quot;pic&quot;</span>, <span class="hljs-string">&quot;memory&quot;</span>);<br><br>        stringRedisTemplate.opsForValue().set(redisKey, <span class="hljs-string">&quot;memory&quot;</span>, <span class="hljs-number">20</span>, TimeUnit.HOURS);<br>        redisTemplate.opsForValue().set(redisKey + <span class="hljs-string">&quot;2&quot;</span>, <span class="hljs-string">&quot;memory2&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="这里注意："><a href="#这里注意：" class="headerlink" title="这里注意："></a>这里注意：</h5></li>
<li><h5 id="RedisTemplate-底层的序列化方式，会导致存入的序列化后的-value-值成为乱码"><a href="#RedisTemplate-底层的序列化方式，会导致存入的序列化后的-value-值成为乱码" class="headerlink" title="RedisTemplate 底层的序列化方式，会导致存入的序列化后的 value 值成为乱码"></a>RedisTemplate 底层的序列化方式，会导致存入的序列化后的 value 值成为乱码</h5></li>
<li><h5 id="StringRedisTemplate-继承了-RedisTemplate-有效解决了这个问题，但只能存放-lt-String，String-gt"><a href="#StringRedisTemplate-继承了-RedisTemplate-有效解决了这个问题，但只能存放-lt-String，String-gt" class="headerlink" title="StringRedisTemplate 继承了 RedisTemplate 有效解决了这个问题，但只能存放&lt;String，String&gt;"></a>StringRedisTemplate 继承了 RedisTemplate 有效解决了这个问题，但只能存放&lt;String，String&gt;</h5></li>
<li><h5 id="综上，我们在使用-Redis-缓存技术时，可以自己自定义（封装一个）RedisTemplate"><a href="#综上，我们在使用-Redis-缓存技术时，可以自己自定义（封装一个）RedisTemplate" class="headerlink" title="综上，我们在使用 Redis 缓存技术时，可以自己自定义（封装一个）RedisTemplate"></a>综上，我们在使用 Redis 缓存技术时，可以自己自定义（封装一个）RedisTemplate</h5></li>
<li><h5 id="自定义-RedisTemplate-lt-String-Object-gt-config-RedisTemplateConfig"><a href="#自定义-RedisTemplate-lt-String-Object-gt-config-RedisTemplateConfig" class="headerlink" title="自定义 RedisTemplate&lt;String, Object&gt; (config/RedisTemplateConfig)"></a>自定义 RedisTemplate&lt;String, Object&gt; (config/RedisTemplateConfig)</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedisTemplateConfig</span> &#123;<br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> RedisTemplate&lt;String, Object&gt; <span class="hljs-title function_">redisTemplate</span><span class="hljs-params">(RedisConnectionFactory connectionFactory)</span> &#123;<br>        <span class="hljs-comment">// 1.创建RedisTemplate对象</span><br>        RedisTemplate&lt;String, Object&gt; redisTemplate = <span class="hljs-keyword">new</span> <span class="hljs-title class_">RedisTemplate</span>&lt;&gt;();<br>        <span class="hljs-comment">// 2.设置连接工厂</span><br>        redisTemplate.setConnectionFactory(connectionFactory);<br>        <span class="hljs-comment">// 3.设置Key的序列化</span><br>        redisTemplate.setKeySerializer(RedisSerializer.string());<br>        <span class="hljs-comment">// 4.创建JSON序列化工具</span><br>        <span class="hljs-type">GenericJackson2JsonRedisSerializer</span> <span class="hljs-variable">jsonRedisSerializer</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">GenericJackson2JsonRedisSerializer</span>();<br>        <span class="hljs-comment">// 5.设置value的序列化</span><br>        redisTemplate.setValueSerializer(jsonRedisSerializer);<br>        <span class="hljs-keyword">return</span> redisTemplate;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="解决-RedisTemplate-存入的序列化后的-value-值成为乱码的问题（2023-08-07-早）"><a href="#解决-RedisTemplate-存入的序列化后的-value-值成为乱码的问题（2023-08-07-早）" class="headerlink" title="解决 RedisTemplate 存入的序列化后的 value 值成为乱码的问题（2023/08/07 早）"></a>解决 RedisTemplate 存入的序列化后的 value 值成为乱码的问题（2023/08/07 早）</h5></li>
</ul>
<h3 id="接入阿里云对象存储服务"><a href="#接入阿里云对象存储服务" class="headerlink" title="接入阿里云对象存储服务"></a>接入阿里云对象存储服务</h3><ul>
<li><h5 id="直接看官方文档，写的很全，傻子都会：（2023-08-09-午）"><a href="#直接看官方文档，写的很全，傻子都会：（2023-08-09-午）" class="headerlink" title="直接看官方文档，写的很全，傻子都会：（2023/08/09 午）"></a>直接看官方文档，写的很全，傻子都会：（2023/08/09 午）</h5></li>
<li><p><a target="_blank" rel="noopener" href="https://developer.aliyun.com/article/1227235">【案例实战】SpringBoot 整合阿里云文件上传 OSS-阿里云开发者社区 (aliyun.com)</a></p>
</li>
<li><h5 id="注意以下几点即可："><a href="#注意以下几点即可：" class="headerlink" title="注意以下几点即可："></a>注意以下几点即可：</h5><ul>
<li>yaml 配置信息保证无误：</li>
</ul>
<figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs nestedtext"><span class="hljs-comment"># 阿里云OSS配置</span><br><span class="hljs-attribute">aliyun</span><span class="hljs-punctuation">:</span><br>  <span class="hljs-attribute">oss</span><span class="hljs-punctuation">:</span><br>    <span class="hljs-attribute">end-point</span><span class="hljs-punctuation">:</span> <span class="hljs-string">oss-cn-beijing.aliyuncs.com</span><br>    <span class="hljs-attribute">access-key-id</span><span class="hljs-punctuation">:</span> <span class="hljs-string">LTAI5t9bAnh9zthSmzTPARM4</span><br>    <span class="hljs-attribute">access-key-secret</span><span class="hljs-punctuation">:</span> <span class="hljs-string">BwV3YKjHp4W2Hk6hYXx1FLbDMO3Qnz</span><br>    <span class="hljs-attribute">bucket-name</span><span class="hljs-punctuation">:</span> <span class="hljs-string">pic-memories</span><br></code></pre></td></tr></table></figure>

<ul>
<li>记得开启对应 Bucket 的公共读写权限：</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230809182044631.png" alt="image-20230809182044631"></p>
</li>
</ul>
<h3 id="自定义-Banner"><a href="#自定义-Banner" class="headerlink" title="自定义 Banner"></a>自定义 Banner</h3><ul>
<li><p>这是 IDEA 一个很有意思的玩法，自定义项目启动时的 banner，详情可以看这篇文章：</p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7259965990960201787">Spring Boot 自定义启动画面 - 掘金 (juejin.cn)</a></p>
</li>
<li><h5 id="我在-banner-txt-添加的文本如下："><a href="#我在-banner-txt-添加的文本如下：" class="headerlink" title="我在 banner.txt 添加的文本如下："></a>我在 banner.txt 添加的文本如下：</h5></li>
</ul>
<figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs txt">启动成功！<br>$&#123;AnsiColor.BRIGHT_GREEN&#125;<br>   *<br> (  `<br> )\))(    (    )       (   (<br>((_)()\  ))\  (     (  )(  )\ )<br>(_()((_)/((_) )\  &#x27; )\(()\(()/(<br>|  \/  (_)) _((_)) ((_)((_))(_))<br>| |\/| / -_) &#x27;  \() _ \ &#x27;_| || |<br>|_|  |_\___|_|_|_|\___/_|  \_, |<br>                           |__/<br>$&#123;AnsiColor.BRIGHT_WHITE&#125;<br>欢迎使用～<br>spring boot 版本为 $&#123;spring-boot.version&#125;<br>作者：@Memory<br>项目名：PicMemories<br>线上访问地址: 未完成<br><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="获取艺术字：-Text-to-ASCII-Art-Generator-TAAG-patorjk-com-http-patorjk-com-software-taag-p-testall-amp-f-Fire-Font-k-amp-t-Memory"><a href="#获取艺术字：-Text-to-ASCII-Art-Generator-TAAG-patorjk-com-http-patorjk-com-software-taag-p-testall-amp-f-Fire-Font-k-amp-t-Memory" class="headerlink" title="获取艺术字：[Text to ASCII Art Generator (TAAG) (patorjk.com)](http://patorjk.com/software/taag/#p=testall&amp;f=Fire Font-k&amp;t=Memory)"></a>获取艺术字：[Text to ASCII Art Generator (TAAG) (patorjk.com)](<a target="_blank" rel="noopener" href="http://patorjk.com/software/taag/#p=testall&amp;f=Fire">http://patorjk.com/software/taag/#p=testall&amp;f=Fire</a> Font-k&amp;t=Memory)</h5></li>
<li><h5 id="项目启动类处，可以控制是否选择加载-banner-txt-文件：（2023-08-12-早）"><a href="#项目启动类处，可以控制是否选择加载-banner-txt-文件：（2023-08-12-早）" class="headerlink" title="项目启动类处，可以控制是否选择加载 banner.txt 文件：（2023/08/12 早）"></a>项目启动类处，可以控制是否选择加载 banner.txt 文件：（2023/08/12 早）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> org.springframework.boot.Banner;<br><span class="hljs-keyword">import</span> org.springframework.boot.SpringApplication;<br><span class="hljs-keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;<br><br><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LaunchApplication</span> &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        <span class="hljs-type">SpringApplication</span> <span class="hljs-variable">app</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SpringApplication</span>(LaunchApplication.class);<br>        app.setBannerMode(Banner.Mode.OFF); <span class="hljs-comment">// 设置 Banner 模式为关闭</span><br>        app.run(args);<br>    &#125;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="主键自增长"><a href="#主键自增长" class="headerlink" title="主键自增长"></a>主键自增长</h3><ul>
<li><h5 id="今天使用-MybatisPlus-执行插入数据时，发现了这样的报错：（2023-08-15-午）"><a href="#今天使用-MybatisPlus-执行插入数据时，发现了这样的报错：（2023-08-15-午）" class="headerlink" title="今天使用 MybatisPlus 执行插入数据时，发现了这样的报错：（2023/08/15 午）"></a>今天使用 MybatisPlus 执行插入数据时，发现了这样的报错：（2023/08/15 午）</h5></li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash">org.springframework.jdbc.BadSqlGrammarException:<br><span class="hljs-comment">### Error updating database.  Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near &#x27;like  ( id,</span><br>user_id,<br>wallpaper_id )  VALUES  ( 1691349539878477825,<br>168799521293<span class="hljs-string">&#x27; at line 1</span><br><span class="hljs-string">### The error may exist in com/memory/picmemories/mapper/LikeMapper.java (best guess)</span><br><span class="hljs-string">### The error may involve com.memory.picmemories.mapper.LikeMapper.insert-Inline</span><br><span class="hljs-string">### The error occurred while setting parameters</span><br></code></pre></td></tr></table></figure>

<ul>
<li>在上面的栏目《表记录/实体类 ID 自增长》中，已经实现了如何使记录中的 id 自增长，这次我就犯了这个错误：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java">create table `like`<br>(<br>    id           bigint primary key comment <span class="hljs-string">&#x27;点赞id&#x27;</span>,<br>    user_id      bigint                                 not <span class="hljs-literal">null</span> comment <span class="hljs-string">&#x27;点赞用户id&#x27;</span>,<br>    wallpaper_id bigint                                 not <span class="hljs-literal">null</span> comment <span class="hljs-string">&#x27;被点赞的壁纸id&#x27;</span>,<br>    create_time  datetime     <span class="hljs-keyword">default</span> CURRENT_TIMESTAMP not <span class="hljs-literal">null</span> comment <span class="hljs-string">&#x27;创建时间&#x27;</span>,<br>    update_time  datetime     <span class="hljs-keyword">default</span> CURRENT_TIMESTAMP not <span class="hljs-literal">null</span> comment <span class="hljs-string">&#x27;更新时间&#x27;</span>,<br>    is_delete    <span class="hljs-title function_">varchar</span><span class="hljs-params">(<span class="hljs-number">256</span>)</span> <span class="hljs-keyword">default</span> <span class="hljs-string">&#x27;0&#x27;</span>               not <span class="hljs-literal">null</span> comment <span class="hljs-string">&#x27;是否删除&#x27;</span><br>)<br>    comment <span class="hljs-string">&#x27;点赞信息&#x27;</span>;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">  * 点赞id</span><br><span class="hljs-comment">  */</span><br> <span class="hljs-meta">@TableId(type = IdType.ASSIGN_ID)</span><br> <span class="hljs-keyword">private</span> Long id;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 4.新增记录</span><br> <span class="hljs-type">Like</span> <span class="hljs-variable">like</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Like</span>();<br> like.setUserId(userId);<br> like.setWallpaperId(wallpaperId);<br><br> <span class="hljs-type">boolean</span> <span class="hljs-variable">save</span> <span class="hljs-operator">=</span> likeService.save(like);<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="如上，很显然，我没有将-id-字段设置为自增长，所以才会出现这样的报错（2023-08-15-午）"><a href="#如上，很显然，我没有将-id-字段设置为自增长，所以才会出现这样的报错（2023-08-15-午）" class="headerlink" title="如上，很显然，我没有将 id 字段设置为自增长，所以才会出现这样的报错（2023/08/15 午）"></a>如上，很显然，我没有将 id 字段设置为自增长，所以才会出现这样的报错（2023/08/15 午）</h5></li>
</ul>
<h3 id="修改表字段后要做的那些事"><a href="#修改表字段后要做的那些事" class="headerlink" title="修改表字段后要做的那些事"></a>修改表字段后要做的那些事</h3><ul>
<li><p>修改表字段后，应该做哪些事？</p>
<ul>
<li>修改对应实体属性</li>
<li>修改 XXXmapper.xml 文件（mapper 路径、domain 路径）</li>
</ul>
</li>
<li><h5 id="使用插件-MybatisX-Generator-快速生成-mapper、domain、service（2023-08-16-午）"><a href="#使用插件-MybatisX-Generator-快速生成-mapper、domain、service（2023-08-16-午）" class="headerlink" title="使用插件 MybatisX-Generator 快速生成 mapper、domain、service（2023/08/16 午）"></a>使用插件 MybatisX-Generator 快速生成 mapper、domain、service（2023/08/16 午）</h5></li>
</ul>
<h3 id="YAML-配置重复键"><a href="#YAML-配置重复键" class="headerlink" title="YAML 配置重复键"></a>YAML 配置重复键</h3><ul>
<li><h5 id="启动项目报了这样的错误：（2023-08-23-晚）"><a href="#启动项目报了这样的错误：（2023-08-23-晚）" class="headerlink" title="启动项目报了这样的错误：（2023/08/23 晚）"></a>启动项目报了这样的错误：（2023/08/23 晚）</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230823164029471.png" alt="image-20230823164029471"></p>
<ul>
<li><h5 id="这个错误通常是由于在-YAML-配置文件中多次定义了相同的键，检查-yaml-配置即可"><a href="#这个错误通常是由于在-YAML-配置文件中多次定义了相同的键，检查-yaml-配置即可" class="headerlink" title="这个错误通常是由于在 YAML 配置文件中多次定义了相同的键，检查 yaml 配置即可"></a>这个错误通常是由于在 YAML 配置文件中多次定义了相同的键，检查 yaml 配置即可</h5></li>
</ul>
<h3 id="git-合并提交"><a href="#git-合并提交" class="headerlink" title="git 合并提交"></a>git 合并提交</h3><ul>
<li><h5 id="在上午，我使用-git-进行合并提交项目代码后，下午运行项目，代码变成了这样："><a href="#在上午，我使用-git-进行合并提交项目代码后，下午运行项目，代码变成了这样：" class="headerlink" title="在上午，我使用 git 进行合并提交项目代码后，下午运行项目，代码变成了这样："></a>在上午，我使用 git 进行合并提交项目代码后，下午运行项目，代码变成了这样：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230826182411757.png" alt="image-20230826182411757"></p>
<ul>
<li><h5 id="没什么大问题，这是因为："><a href="#没什么大问题，这是因为：" class="headerlink" title="没什么大问题，这是因为："></a>没什么大问题，这是因为：</h5></li>
<li><h5 id="你使用-git-合并推送项目代码后遇到了冲突（conflict），冲突的部分被-Git-标记为-lt-lt-lt-lt-lt-lt-lt-HEAD-和-gt-gt-gt-gt-gt-gt-gt-。"><a href="#你使用-git-合并推送项目代码后遇到了冲突（conflict），冲突的部分被-Git-标记为-lt-lt-lt-lt-lt-lt-lt-HEAD-和-gt-gt-gt-gt-gt-gt-gt-。" class="headerlink" title="你使用 git 合并推送项目代码后遇到了冲突（conflict），冲突的部分被 Git 标记为&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD, =======和&gt;&gt;&gt;&gt;&gt;&gt;&gt;。"></a>你使用 git 合并推送项目代码后遇到了冲突（conflict），冲突的部分被 Git 标记为&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD, =======和&gt;&gt;&gt;&gt;&gt;&gt;&gt;。</h5></li>
<li><h5 id="这是因为在合并时，Git-无法确定如何自动合并这些不同版本的代码。2023-08-26-晚）"><a href="#这是因为在合并时，Git-无法确定如何自动合并这些不同版本的代码。2023-08-26-晚）" class="headerlink" title="这是因为在合并时，Git 无法确定如何自动合并这些不同版本的代码。2023/08/26 晚）"></a>这是因为在合并时，Git 无法确定如何自动合并这些不同版本的代码。2023/08/26 晚）</h5></li>
<li><p>为解决冲突，你需要手动编辑冲突的文件，然后再次提交。以下是我采取的一些步骤：</p>
<ul>
<li>打开标记有冲突的文件，找到&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD, =======和&gt;&gt;&gt;&gt;&gt;&gt;&gt;之间的冲突代码段。</li>
<li>理解每个版本的更改，并决定要保留哪些部分。可以选择保留某个版本的代码，或者进行适当的修改以使两个版本的更改合并。</li>
<li>对冲突的代码段进行适当修改，解决冲突。</li>
<li>删除冲突标记（&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD, =======和&gt;&gt;&gt;&gt;&gt;&gt;&gt;）。</li>
<li>完成修改后，保存文件。</li>
</ul>
</li>
<li><h5 id="完成（2023-08-26-午）"><a href="#完成（2023-08-26-午）" class="headerlink" title="完成（2023/08/26 午）"></a>完成（2023/08/26 午）</h5></li>
</ul>
<h3 id="Spring-项目起不来"><a href="#Spring-项目起不来" class="headerlink" title="Spring 项目起不来"></a>Spring 项目起不来</h3><ul>
<li><h5 id="这个栏目标题起的有点怪啊，不过确实是这么回事：（2023-08-29-早）"><a href="#这个栏目标题起的有点怪啊，不过确实是这么回事：（2023-08-29-早）" class="headerlink" title="这个栏目标题起的有点怪啊，不过确实是这么回事：（2023/08/29 早）"></a>这个栏目标题起的有点怪啊，不过确实是这么回事：（2023/08/29 早）</h5></li>
<li><p>今早计划测试一把 Spring 的定时任务功能，结果构建完成一个 SpringBoot 项目之后，项目却启动不起来：</p>
</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230829113246414.png" alt="image-20230829113246414"></p>
<ul>
<li><h5 id="准确的说，项目没有启动成为一个-Web-服务器后台，这是为什么呢？"><a href="#准确的说，项目没有启动成为一个-Web-服务器后台，这是为什么呢？" class="headerlink" title="准确的说，项目没有启动成为一个 Web 服务器后台，这是为什么呢？"></a>准确的说，项目没有启动成为一个 Web 服务器后台，这是为什么呢？</h5></li>
<li><p>妈的，原来是构建项目时，忘记导入相关依赖了：</p>
</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230829112442434.png" alt="image-20230829112442434"></p>
<ul>
<li>即：</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="呐，导入以上依赖后，SpringBoot-成功运行起来了：（2023-08-29-早）"><a href="#呐，导入以上依赖后，SpringBoot-成功运行起来了：（2023-08-29-早）" class="headerlink" title="呐，导入以上依赖后，SpringBoot 成功运行起来了：（2023/08/29 早）"></a>呐，导入以上依赖后，SpringBoot 成功运行起来了：（2023/08/29 早）</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230829112724037.png" alt="image-20230829112724037"></p>
<ul>
<li><p>真是学傻了，这么基础的问题，平时竟然没注意到，呵呵呵</p>
</li>
<li><h5 id="顺带提一下，SpringBoot-项目构建完成后，默认导入以下两个依赖："><a href="#顺带提一下，SpringBoot-项目构建完成后，默认导入以下两个依赖：" class="headerlink" title="顺带提一下，SpringBoot 项目构建完成后，默认导入以下两个依赖："></a>顺带提一下，SpringBoot 项目构建完成后，默认导入以下两个依赖：</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>         <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>         <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>     <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>     <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>         <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>         <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>         <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span><br>     <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<h3 id="Spring-项目起不来（2）"><a href="#Spring-项目起不来（2）" class="headerlink" title="Spring 项目起不来（2）"></a>Spring 项目起不来（2）</h3><ul>
<li><h5 id="这次是什么原因呢？看报错：（2023-09-06）"><a href="#这次是什么原因呢？看报错：（2023-09-06）" class="headerlink" title="这次是什么原因呢？看报错：（2023/09/06）"></a>这次是什么原因呢？看报错：（2023/09/06）</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230906172719883.png" alt="image-20230906172719883"></p>
<figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs livecodeserver">***************************<br>APPLICATION FAILED TO START<br>***************************<br><br>Description:<br><br>Failed <span class="hljs-built_in">to</span> configure <span class="hljs-keyword">a</span> DataSource: <span class="hljs-string">&#x27;url&#x27;</span> attribute is <span class="hljs-keyword">not</span> specified <span class="hljs-keyword">and</span> no embedded datasource could be configured.<br><br>Reason: Failed <span class="hljs-built_in">to</span> determine <span class="hljs-keyword">a</span> suitable driver class<br><br>Action:<br><br>Consider <span class="hljs-keyword">the</span> following:<br>	If you want <span class="hljs-keyword">an</span> embedded database (H2, HSQL <span class="hljs-keyword">or</span> Derby), please <span class="hljs-built_in">put</span> <span class="hljs-keyword">it</span> <span class="hljs-keyword">on</span> <span class="hljs-title">the</span> <span class="hljs-title">classpath</span>.<br>	If you have database settings <span class="hljs-built_in">to</span> be loaded <span class="hljs-built_in">from</span> <span class="hljs-keyword">a</span> particular profile you may need <span class="hljs-built_in">to</span> activate <span class="hljs-keyword">it</span> (no profiles are currently active).<br><br>Process finished <span class="hljs-keyword">with</span> exit code <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="这个就很清晰了，因为引入了数据库相关依赖，却没有作相关配置："><a href="#这个就很清晰了，因为引入了数据库相关依赖，却没有作相关配置：" class="headerlink" title="这个就很清晰了，因为引入了数据库相关依赖，却没有作相关配置："></a>这个就很清晰了，因为引入了数据库相关依赖，却没有作相关配置：</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.2.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>       <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.baomidou<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mybatis-plus-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.5.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>       <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>8.0.31<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">datasource:</span><br>    <span class="hljs-attr">driver-class-name:</span> <span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br>    <span class="hljs-attr">url:</span> <span class="hljs-string">jdbc:mysql://localhost:3306/memory_api</span><br>    <span class="hljs-attr">username:</span> <span class="hljs-string">root</span><br>    <span class="hljs-attr">password:</span> <span class="hljs-string">Dw990831</span><br></code></pre></td></tr></table></figure>

<h3 id="mapper-注入为-null"><a href="#mapper-注入为-null" class="headerlink" title="mapper 注入为 null"></a>mapper 注入为 null</h3><ul>
<li><h5 id="从来没有踩过这个坑，这次让我记忆深刻了（2023-09-07-晚）"><a href="#从来没有踩过这个坑，这次让我记忆深刻了（2023-09-07-晚）" class="headerlink" title="从来没有踩过这个坑，这次让我记忆深刻了（2023/09/07 晚）"></a>从来没有踩过这个坑，这次让我记忆深刻了（2023/09/07 晚）</h5></li>
<li><p>如图，我注入了 interfaceInfoMapper</p>
</li>
</ul>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230907214553078.png" alt="image-20230907214553078"></p>
<ul>
<li>但项目运行后，通过 debug 发现，该 interfaceInfoMapper 为 null，为什么注入不成功呢？我犯了一个错误：</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230907214839239.png" alt="image-20230907214839239"></p>
<ul>
<li>如上，我 new 了一个 interfaceIdSource，这个问题被我忽视了：</li>
</ul>
<figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs haxe">类的实例化问题：确保你在使用 InterfaceIdSource 类时是通过 Spring 容器来获取实例，而不是通过 <span class="hljs-keyword">new</span> <span class="hljs-type"></span>关键字手动创建对象。只有通过 Spring 容器管理的对象才会进行自动注入。<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="直接注入即-interfaceIdSource-可解决问题：（2023-09-07-晚）"><a href="#直接注入即-interfaceIdSource-可解决问题：（2023-09-07-晚）" class="headerlink" title="直接注入即 interfaceIdSource 可解决问题：（2023/09/07 晚）"></a>直接注入即 interfaceIdSource 可解决问题：（2023/09/07 晚）</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230907215126113.png" alt="image-20230907215126113"></p>
<h3 id="MybatisPlus-踩坑记录"><a href="#MybatisPlus-踩坑记录" class="headerlink" title="MybatisPlus 踩坑记录"></a>MybatisPlus 踩坑记录</h3><ul>
<li>我在根据 id 查询用户时，写了如下代码：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">for</span> (Friends friends : friendsList) &#123;<br>           <span class="hljs-type">Long</span> <span class="hljs-variable">friendId</span> <span class="hljs-operator">=</span> friends.getFriendId();<br>           uqw.eq(<span class="hljs-string">&quot;id&quot;</span>, friendId);<br>           <span class="hljs-type">User</span> <span class="hljs-variable">one</span> <span class="hljs-operator">=</span> userService.getOne(uqw);<br>           userList.add(one);<br>       &#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><p>结果，查出来的 one 对象均为 null，可能是因为 id 设置为了主键</p>
</li>
<li><h5 id="我们改写为-MybatisPlus-提供的的根据-id-查询方法，成功解决问题（2023-09-12-午）"><a href="#我们改写为-MybatisPlus-提供的的根据-id-查询方法，成功解决问题（2023-09-12-午）" class="headerlink" title="我们改写为 MybatisPlus 提供的的根据 id 查询方法，成功解决问题（2023/09/12 午）"></a>我们改写为 MybatisPlus 提供的的根据 id 查询方法，成功解决问题（2023/09/12 午）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 2.根据id查询道好友信息</span><br>     <span class="hljs-keyword">for</span> (Friends friends : friendsList) &#123;<br>         <span class="hljs-type">Long</span> <span class="hljs-variable">friendId</span> <span class="hljs-operator">=</span> friends.getFriendId();<br>         <span class="hljs-type">User</span> <span class="hljs-variable">one</span> <span class="hljs-operator">=</span> userService.getById(friendId);<br>         userList.add(one);<br>     &#125;<br></code></pre></td></tr></table></figure>

<h3 id="日常犯傻"><a href="#日常犯傻" class="headerlink" title="日常犯傻"></a>日常犯傻</h3><ul>
<li>使用 Vue 的 ref() 语法时，容易忘记取.value</li>
</ul>
<figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf">const currentUserId <span class="hljs-operator">=</span> currentUser.value.id<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure>

<ul>
<li>访问后端服务器路径，容易忘记写/api</li>
</ul>
<figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">const socketUrl = `ws:<span class="hljs-regexp">//</span>localhost:<span class="hljs-number">8081</span><span class="hljs-regexp">/api/</span>websocket/<span class="hljs-variable">$&#123;currentUserId&#125;</span>`;<br></code></pre></td></tr></table></figure>

<h3 id="导入-Excel-文件"><a href="#导入-Excel-文件" class="headerlink" title="导入 Excel 文件"></a>导入 Excel 文件</h3><ul>
<li><h5 id="Easyexcel-官网：（2023-10-05-午）"><a href="#Easyexcel-官网：（2023-10-05-午）" class="headerlink" title="Easyexcel 官网：（2023/10/05 午）"></a>Easyexcel 官网：（2023/10/05 午）</h5></li>
<li><h5 id="关于-Easyexcel-Easy-Excel-alibaba-com"><a href="#关于-Easyexcel-Easy-Excel-alibaba-com" class="headerlink" title="关于 Easyexcel | Easy Excel (alibaba.com)"></a><a target="_blank" rel="noopener" href="https://easyexcel.opensource.alibaba.com/docs/current/">关于 Easyexcel | Easy Excel (alibaba.com)</a></h5></li>
<li><h5 id="简单的测试："><a href="#简单的测试：" class="headerlink" title="简单的测试："></a>简单的测试：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">File</span> <span class="hljs-variable">file</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br>      <span class="hljs-keyword">try</span> &#123;<br>          file = ResourceUtils.getFile(<span class="hljs-string">&quot;classpath:test_excel.xlsx&quot;</span>);<br>      &#125; <span class="hljs-keyword">catch</span> (FileNotFoundException e) &#123;<br>          e.printStackTrace();<br>      &#125;<br>      <span class="hljs-comment">// 读取数据</span><br>      List&lt;Map&lt;Integer, String&gt;&gt; list = <span class="hljs-literal">null</span>;<br><br>      list = EasyExcel.read(file)<br>              .excelType(ExcelTypeEnum.XLSX)<br>              .sheet()<br>              .headRowNumber(<span class="hljs-number">0</span>)<br>              .doReadSync();<br><br>      <span class="hljs-keyword">if</span> (CollUtil.isEmpty(list)) &#123;<br>          <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;&quot;</span>;<br>      &#125;<br></code></pre></td></tr></table></figure>

<ul>
<li>这部分代码，简单地读取了 <strong>resourse 目录下</strong>的 <strong>test_excel.xlsx 文件</strong>，并<strong>成功获取表格数据</strong></li>
<li>附上原表格数据和解析效果：</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231005162100413.png" alt="image-20231005162100413"></p>
<hr>
<p><img src="http://blog.memory-life.xyz/image-20231005162143753.png" alt="image-20231005162143753"></p>
<ul>
<li><h5 id="修改代码，接收上传的文件："><a href="#修改代码，接收上传的文件：" class="headerlink" title="修改代码，接收上传的文件："></a>修改代码，接收上传的文件：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 智能分析</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> multipartFile       Excel文件</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> genChartByAiRequest 指定图表信息</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> request             request</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 生成的图表信息</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@PostMapping(&quot;/generate&quot;)</span><br><span class="hljs-keyword">public</span> BaseResponse&lt;String&gt; <span class="hljs-title function_">uploadFile</span><span class="hljs-params">(<span class="hljs-meta">@RequestPart(&quot;file&quot;)</span> MultipartFile multipartFile,</span><br><span class="hljs-params">                                       GenChartByAiRequest genChartByAiRequest, HttpServletRequest request)</span> &#123;<br>    <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> genChartByAiRequest.getName();<br>    <span class="hljs-type">String</span> <span class="hljs-variable">goal</span> <span class="hljs-operator">=</span> genChartByAiRequest.getGoal();<br>    <span class="hljs-type">String</span> <span class="hljs-variable">chartType</span> <span class="hljs-operator">=</span> genChartByAiRequest.getChartType();<br><br>    <span class="hljs-comment">// 校验</span><br>    ThrowUtils.throwIf(StringUtils.isBlank(goal), ErrorCode.PARAMS_ERROR, <span class="hljs-string">&quot;目标为空&quot;</span>);<br>    ThrowUtils.throwIf(StringUtils.isNotBlank(name) &amp;&amp; name.length() &gt; <span class="hljs-number">100</span>, ErrorCode.PARAMS_ERROR, <span class="hljs-string">&quot;名称过长&quot;</span>);<br><br>    <span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> ExcelUtils.excelToCsv(multipartFile);<br>    <span class="hljs-keyword">return</span> ResultUtils.success(result);<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="逐行解析-Excle-表格，获取数据："><a href="#逐行解析-Excle-表格，获取数据：" class="headerlink" title="逐行解析 Excle 表格，获取数据："></a>逐行解析 Excle 表格，获取数据：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">   * excel 转 csv</span><br><span class="hljs-comment">   *</span><br><span class="hljs-comment">   * <span class="hljs-doctag">@param</span> multipartFile</span><br><span class="hljs-comment">   * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">   */</span><br>  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">excelToCsv</span><span class="hljs-params">(MultipartFile multipartFile)</span> &#123;<br>      <span class="hljs-type">File</span> <span class="hljs-variable">file</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br>      <span class="hljs-keyword">try</span> &#123;<br>          file = ResourceUtils.getFile(<span class="hljs-string">&quot;classpath:test_excel.xlsx&quot;</span>);<br>      &#125; <span class="hljs-keyword">catch</span> (FileNotFoundException e) &#123;<br>          e.printStackTrace();<br>      &#125;<br>      <span class="hljs-comment">// 读取数据</span><br>      List&lt;Map&lt;Integer, String&gt;&gt; list = <span class="hljs-literal">null</span>;<br>      list = EasyExcel.read(file)<br>              .excelType(ExcelTypeEnum.XLSX)<br>              .sheet()<br>              .headRowNumber(<span class="hljs-number">0</span>)<br>              .doReadSync();<br>      <span class="hljs-keyword">if</span> (CollUtil.isEmpty(list)) &#123;<br>          <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;&quot;</span>;<br>      &#125;<br>      <span class="hljs-comment">// 转换为 csv</span><br>      <span class="hljs-type">StringBuilder</span> <span class="hljs-variable">stringBuilder</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>();<br>      <span class="hljs-comment">// 读取表头</span><br>      LinkedHashMap&lt;Integer, String&gt; headerMap = (LinkedHashMap) list.get(<span class="hljs-number">0</span>);<br>      List&lt;String&gt; headerList = headerMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());<br>      stringBuilder.append(StringUtils.join(headerList, <span class="hljs-string">&quot;,&quot;</span>)).append(<span class="hljs-string">&quot;\n&quot;</span>);<br>      <span class="hljs-comment">// 读取数据</span><br>      <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i &lt; list.size(); i++) &#123;<br>          LinkedHashMap&lt;Integer, String&gt; dataMap = (LinkedHashMap) list.get(i);<br>          List&lt;String&gt; dataList = dataMap.values().stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());<br>          stringBuilder.append(StringUtils.join(dataList, <span class="hljs-string">&quot;,&quot;</span>)).append(<span class="hljs-string">&quot;\n&quot;</span>);<br>      &#125;<br>      <span class="hljs-keyword">return</span> stringBuilder.toString();<br>  &#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="，我们在接口文档中，上传-Excel-文件："><a href="#，我们在接口文档中，上传-Excel-文件：" class="headerlink" title="，我们在接口文档中，上传 Excel 文件："></a>，我们在接口文档中，上传 Excel 文件：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231005161702354.png" alt="image-20231005161702354"></p>
<ul>
<li><h5 id="成功解析获取数据并返回："><a href="#成功解析获取数据并返回：" class="headerlink" title="成功解析获取数据并返回："></a>成功解析获取数据并返回：</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231005161948245.png" alt="image-20231005161948245"></p>
<h3 id="校验文件"><a href="#校验文件" class="headerlink" title="校验文件"></a>校验文件</h3><ul>
<li><p>只要涉及到<strong>用户自主上传</strong>操作，一定要校验文件<strong>（图像）</strong></p>
</li>
<li><p>校验：</p>
<ul>
<li><strong>文件大小</strong></li>
<li><strong>文件后缀</strong></li>
<li><strong>文件内容</strong>（成本略高）</li>
<li><strong>文件合规</strong>（敏感内容）</li>
</ul>
</li>
<li><h5 id="校验文件大小和文件后缀"><a href="#校验文件大小和文件后缀" class="headerlink" title="校验文件大小和文件后缀"></a>校验文件大小和文件后缀</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 允许上传的文件大小</span><br><span class="hljs-comment">    */</span><br>   <span class="hljs-type">long</span> <span class="hljs-variable">ONE_MB</span> <span class="hljs-operator">=</span> <span class="hljs-number">1024</span> * <span class="hljs-number">1024L</span>;<br><br><br>   <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 合法的文件后缀</span><br><span class="hljs-comment">    */</span><br>   List&lt;String&gt; VALID_FILE_SUFFIX_LIST = Arrays.asList(<span class="hljs-string">&quot;xlsx&quot;</span>, <span class="hljs-string">&quot;xls&quot;</span>);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 3.1.校验文件</span><br>        <span class="hljs-comment">// 3.1.1.校验文件大小</span><br>        <span class="hljs-type">long</span> <span class="hljs-variable">size</span> <span class="hljs-operator">=</span> multipartFile.getSize();<br>        ThrowUtils.throwIf(size &gt; ONE_MB, ErrorCode.PARAMS_ERROR, <span class="hljs-string">&quot;文件超过 1M&quot;</span>);<br><br>        <span class="hljs-comment">// 3.1.2.校验文件后缀</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">originalFilename</span> <span class="hljs-operator">=</span> multipartFile.getOriginalFilename();<br>        <span class="hljs-type">String</span> <span class="hljs-variable">suffix</span> <span class="hljs-operator">=</span> FileUtil.getSuffix(originalFilename);<br>        ThrowUtils.throwIf(!VALID_FILE_SUFFIX_LIST.contains(suffix), ErrorCode.PARAMS_ERROR, <span class="hljs-string">&quot;文件后缀非法&quot;</span>);<br></code></pre></td></tr></table></figure>

<h3 id="限流"><a href="#限流" class="headerlink" title="限流"></a>限流</h3><ul>
<li><p>最近实现 <strong>Memory BI 智能分析平台时</strong>，需要限制用户在<strong>单位时间内对智能分析接口的调用次数</strong> <strong>（2023/10/11 晚）</strong></p>
</li>
<li><h5 id="面试必备：4-种经典限流算法讲解-掘金-juejin-cn"><a href="#面试必备：4-种经典限流算法讲解-掘金-juejin-cn" class="headerlink" title="面试必备：4 种经典限流算法讲解 - 掘金 (juejin.cn)"></a><a target="_blank" rel="noopener" href="https://juejin.cn/post/6967742960540581918">面试必备：4 种经典限流算法讲解 - 掘金 (juejin.cn)</a></h5></li>
<li><h5 id="面试官：来，年轻人！请手撸-5-种常见限流算法！-Java-填坑笔记-博客园-cnblogs-com"><a href="#面试官：来，年轻人！请手撸-5-种常见限流算法！-Java-填坑笔记-博客园-cnblogs-com" class="headerlink" title="面试官：来，年轻人！请手撸 5 种常见限流算法！ - Java 填坑笔记 - 博客园 (cnblogs.com)"></a><a target="_blank" rel="noopener" href="https://www.cnblogs.com/liqiangchn/p/14253924.html">面试官：来，年轻人！请手撸 5 种常见限流算法！ - Java 填坑笔记 - 博客园 (cnblogs.com)</a></h5></li>
<li><p>我们使用 <strong>Redisson</strong> 实现的<strong>令牌桶算法</strong>实现：<strong>调用智能分析接口时，限制用户在单位时间内的调用次数</strong></p>
</li>
<li><h5 id="引入依赖："><a href="#引入依赖：" class="headerlink" title="引入依赖："></a>引入依赖：</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.redisson<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>redisson<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>           <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.21.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>       <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="做好相关配置："><a href="#做好相关配置：" class="headerlink" title="做好相关配置："></a>做好相关配置：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@ConfigurationProperties(prefix = &quot;spring.redis&quot;)</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedissionConfig</span> &#123;<br>    <span class="hljs-keyword">private</span> String host;<br><br>    <span class="hljs-keyword">private</span> String port;<br><br>    <span class="hljs-keyword">private</span> String password;<br><br>    <span class="hljs-keyword">private</span> Integer database;<br><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> RedissonClient <span class="hljs-title function_">redissonClient</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-comment">// 1. 创建配置</span><br>        <span class="hljs-type">Config</span> <span class="hljs-variable">config</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Config</span>();<br>        <span class="hljs-type">String</span> <span class="hljs-variable">redisAddress</span> <span class="hljs-operator">=</span> String.format(<span class="hljs-string">&quot;redis://%s:%s&quot;</span>, host, port);<br>        <span class="hljs-comment">//  使用单个Redis，没有开集群 useClusterServers()  设置地址和使用库</span><br>        config.useSingleServer().setAddress(redisAddress).setDatabase(database).setPassword(password);<br>        <span class="hljs-comment">// 2. 创建实例</span><br>        <span class="hljs-keyword">return</span> Redisson.create(config);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">redis:</span><br>  <span class="hljs-attr">port:</span> <span class="hljs-number">6379</span><br>  <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span><br>  <span class="hljs-attr">database:</span> <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="限流实现（区别不同的限流器，每个用户都分别拥有对应的限流器）"><a href="#限流实现（区别不同的限流器，每个用户都分别拥有对应的限流器）" class="headerlink" title="限流实现（区别不同的限流器，每个用户都分别拥有对应的限流器）"></a>限流实现（区别不同的限流器，每个用户都分别拥有对应的限流器）</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 限流实现</span><br><span class="hljs-comment">    *</span><br><span class="hljs-comment">    * <span class="hljs-doctag">@param</span> key 识别用户的key</span><br><span class="hljs-comment">    */</span><br>   <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doRateLimit</span><span class="hljs-params">(String key)</span> &#123;<br>       <span class="hljs-comment">// 创建限流器</span><br>       <span class="hljs-type">RRateLimiter</span> <span class="hljs-variable">rateLimiter</span> <span class="hljs-operator">=</span> redissonClient.getRateLimiter(key);<br>       rateLimiter.trySetRate(RateType.OVERALL, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, RateIntervalUnit.SECONDS);<br>       <span class="hljs-comment">// 每当一个操作来了之后，请求一个令牌</span><br>       <span class="hljs-type">boolean</span> <span class="hljs-variable">canOp</span> <span class="hljs-operator">=</span> rateLimiter.tryAcquire(<span class="hljs-number">1</span>);<br>       <span class="hljs-comment">// 超出发放令牌数目，请求过于频繁</span><br>       ThrowUtils.throwIf(!canOp, ErrorCode.TOO_MANY_REQUEST);<br>   &#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="简单测试，效果良好，测试结果如下："><a href="#简单测试，效果良好，测试结果如下：" class="headerlink" title="简单测试，效果良好，测试结果如下："></a>简单测试，效果良好，测试结果如下：</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br>   <span class="hljs-keyword">void</span> <span class="hljs-title function_">doRateLimit</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException &#123;<br>       <span class="hljs-type">String</span> <span class="hljs-variable">userId</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;1&quot;</span>;<br>       <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">2</span>; i++) &#123;<br>           redisLimiterManager.doRateLimit(userId);<br>           System.out.println(<span class="hljs-string">&quot;成功&quot;</span>);<br>       &#125;<br>       Thread.sleep(<span class="hljs-number">1000</span>);<br>       <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++) &#123;<br>           redisLimiterManager.doRateLimit(userId);<br>           System.out.println(<span class="hljs-string">&quot;成功&quot;</span>);<br>       &#125;<br>   &#125;<br></code></pre></td></tr></table></figure>

<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20231011180516219.png" alt="image-20231011180516219"></p>
<ul>
<li>业务中使用限流器，<strong>调用智能分析接口时，限制用户在单位时间内的调用次数</strong> <strong>（2023/10/11 晚）</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 限流(限制用户的调用次数，以用户id为key，区分各个限流器)</span><br>redisLimiterManager.doRateLimit(<span class="hljs-string">&quot;genCharByAi_&quot;</span> + loginUser.getId());<br></code></pre></td></tr></table></figure>

<h3 id="Slf4j-导入依赖错误"><a href="#Slf4j-导入依赖错误" class="headerlink" title="Slf4j 导入依赖错误"></a>Slf4j 导入依赖错误</h3><ul>
<li>这是个很有意思的问题，起因是我计划<strong>实现一个延迟队列 Demo</strong>，结果在运行时出现了这样的报错：</li>
</ul>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20231019123726686.png" alt="image-20231019123726686"></p>
<ul>
<li>这些报错是什么意思呢？简单解释一下：</li>
</ul>
<blockquote>
<p>这些警告信息是由 SLF4J（Simple Logging Facade for Java）引发的，它是一个为 Java 程序提供日志功能的简单抽象层。</p>
<p>警告 <code>No SLF4J providers were found</code> 表示在类路径上未找到任何 SLF4J 提供者。这意味着没有配置合适的日志库实现，导致程序在运行时无法正常输出日志。</p>
<p>警告 <code>Defaulting to no-operation (NOP) logger implementation</code> 表示在缺少真正的日志库实现时，SLF4J 默认使用了一个空操作的日志记录器实现。这意味着代码在运行时不会输出任何日志。</p>
<p>警告 <code>Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier</code> 表示在类路径上发现了与 SLF4J 绑定，但它们是针对 1.7.x 版本或更早版本的 slf4j-api。这可能导致不兼容或出现日志记录问题。</p>
<p>警告 <code>Ignoring binding found at [jar:file:/D:/softWare/Maven/apache-maven-3.6.1/mvn_resp/ch/qos/logback/logback-classic/1.2.12/logback-classic-1.2.12.jar!/org/slf4j/impl/StaticLoggerBinder.class]</code> 表示 SLF4J 发现了一个冲突的绑定，将被忽略。这意味着在类路径上有多个不同版本的 SLF4J 绑定或日志库实现。</p>
<p>解决这些警告的方法是确保正确配置了 SLF4J 绑定和日志库实现，以及它们的版本兼容性。</p>
</blockquote>
<ul>
<li>同时我也受到了 AI 的点拨，可以从以下几个方面排查问题：</li>
</ul>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">1</span>、确保在项目的依赖管理中添加了正确的 SLF4J 绑定和日志库实现。<br><span class="hljs-attribute">2</span>、确保依赖的版本与所使用的 SLF4J 版本兼容。推荐使用 SLF4J 版本 <span class="hljs-number">1</span>.<span class="hljs-number">7</span>.x 或更高版本。<br><span class="hljs-attribute">3</span>、检查项目的构建路径，确保没有冲突的 SLF4J 绑定存在于类路径上。<br><span class="hljs-attribute">4</span>、如果问题仍然存在，可以尝试在项目中排除掉冲突的 SLF4J 绑定，或通过 Maven 或 Gradle 等构建工具来管理依赖关系。<br><span class="hljs-attribute">5</span>、如果仍然无法解决问题，可以参考 SLF4J 的文档和常见问题页面（https://www.slf4j.org/codes.html）寻找更详细的解决方案。<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="我检查了引入的相关依赖，发现除了-lombok-依赖外，还引入了一个-slf4j-api-依赖："><a href="#我检查了引入的相关依赖，发现除了-lombok-依赖外，还引入了一个-slf4j-api-依赖：" class="headerlink" title="我检查了引入的相关依赖，发现除了 lombok 依赖外，还引入了一个 slf4j-api 依赖："></a>我检查了引入的相关依赖，发现除了 lombok 依赖外，还引入了一个 slf4j-api 依赖：</h5></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.projectlombok<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>lombok<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.slf4j<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>slf4j-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0.5<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>	<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li>这就是问题所在了，<strong>删除这个依赖即可解决问题</strong>，程序成功运行：<strong>（2023/10/19 早）</strong></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231019124205016.png" alt="image-20231019124205016"></p>
<h3 id="实现-updateTime-字段自动更新"><a href="#实现-updateTime-字段自动更新" class="headerlink" title="实现 updateTime 字段自动更新"></a>实现 updateTime 字段自动更新</h3><ul>
<li>什么意思呢？我们希望在<strong>修改完数据库表中的记录</strong>后，该条记录<strong>对应的 uodateTime 字段</strong>实现<strong>自动更新</strong></li>
<li>实现方法很简单，在 IDEA 中，直接修改表的 updateTime 字段属性，如下：</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231107224250849.png" alt="image-20231107224250849"></p>
<ul>
<li>对应的 <strong><code>DDL</code> 语句</strong>是这样的：</li>
</ul>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql">updateTime datetime <span class="hljs-keyword">default</span> <span class="hljs-built_in">CURRENT_TIMESTAMP</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">update</span> <span class="hljs-built_in">CURRENT_TIMESTAMP</span> comment <span class="hljs-string">&#x27;更新时间&#x27;</span>,<br></code></pre></td></tr></table></figure>

<ul>
<li>这样，我们<strong>更新记录</strong>后，该记录 updateTime 字段会<strong>自动更新为最近修改时间</strong>：<strong>（2023/11/07 晚）</strong></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231107224550821.png" alt="image-20231107224550821"></p>
<h3 id="实体类未序列化"><a href="#实体类未序列化" class="headerlink" title="实体类未序列化"></a>实体类未序列化</h3><ul>
<li>我们在后端写接口时，会这样编写接收的参数：<strong>（2023/11/20 晚）</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@PostMapping(&quot;/list/page/vo&quot;)</span><br><span class="hljs-keyword">public</span> BaseResponse&lt;Page&lt;Picture&gt;&gt; <span class="hljs-title function_">listPictureByPage</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> Picture picture)</span> <span class="hljs-keyword">throws</span> IOException &#123;<br>    <span class="hljs-comment">// controller层对参数的校验</span><br>    <span class="hljs-type">String</span> <span class="hljs-variable">category</span> <span class="hljs-operator">=</span> picture.getCategory();<br><br>    Page&lt;Picture&gt; picturePage = pictureService.listPictureVOByPage(category);<br>    <span class="hljs-keyword">return</span> ResultUtils.success(picturePage);<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><p>当然，要理解这种接收参数的编写语法，需要系统学习 SpringMVC 的相关内容，这里只谈一点：</p>
<blockquote>
<h5 id="Picture-要支持序列化才能实现网络传输"><a href="#Picture-要支持序列化才能实现网络传输" class="headerlink" title="Picture 要支持序列化才能实现网络传输"></a><code>Picture</code> 要支持序列化才能实现网络传输</h5></blockquote>
</li>
<li><p>妈的，所以才会出现这样的报错：</p>
</li>
</ul>
<figure class="highlight smali"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs smali">om.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct<span class="hljs-built_in"> instance </span>of `memory.cloud.memoryclient.model.domain.Picture` (no Creators, like default<span class="hljs-keyword"> constructor</span>, exist): cannot deserialize from Object value (no delegate-<span class="hljs-built_in"> or </span>property-based Creator)<br> at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 2]<br></code></pre></td></tr></table></figure>

<ul>
<li>排了一个小时的错，可算发现了：我编写的 Picture 不支持序列化，改写成如下这样即可：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Picture</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">Picture</span><span class="hljs-params">()</span> &#123;<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 所属分类</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String category;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 图片名</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String title;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 图片路径</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String url;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> <span class="hljs-variable">serialVersionUID</span> <span class="hljs-operator">=</span> <span class="hljs-number">1L</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li>平时这种实体类都是使用 MybatisX-Generator 快速生成的，没注意到<strong>实体类要支持序列化</strong> <strong>（2023/11/20 晚）</strong></li>
</ul>
<h3 id="存储数据库编码错误"><a href="#存储数据库编码错误" class="headerlink" title="存储数据库编码错误"></a>存储数据库编码错误</h3><ul>
<li>终于解决了如何正确保存含 <code>emoji</code> 表情数据到数据库中的问题了</li>
<li>直接保存</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><code class="hljs java"> <span class="hljs-comment">// 1. 获取数据</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;https://juejin.cn/post/7313418992310976549&quot;</span>;<br>        <span class="hljs-keyword">try</span> &#123;<br>            <span class="hljs-type">Document</span> <span class="hljs-variable">doc</span> <span class="hljs-operator">=</span> Jsoup.connect(url)<br>                    .userAgent(<span class="hljs-string">&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.81&quot;</span>)<br>                    .get();<br><br><br>            <span class="hljs-type">Elements</span> <span class="hljs-variable">title</span> <span class="hljs-operator">=</span> doc.select(<span class="hljs-string">&quot;.article-area .article-title&quot;</span>);<br>            System.out.println(<span class="hljs-string">&quot;----------------博文标题----------------&quot;</span>);<br>            System.out.println(title.text());<br><br>            <span class="hljs-type">Elements</span> <span class="hljs-variable">content</span> <span class="hljs-operator">=</span> doc.select(<span class="hljs-string">&quot;.article-viewer p&quot;</span>);<br>            <span class="hljs-comment">// for (Element p : content) &#123;</span><br>            <span class="hljs-comment">//     p.select(&quot;img&quot;).remove();</span><br>            <span class="hljs-comment">//     p.select(&quot;a&quot;).remove();</span><br>            <span class="hljs-comment">// &#125;</span><br><br>            System.out.println(<span class="hljs-string">&quot;---------------博文正文------------------&quot;</span>);<br>            <span class="hljs-comment">// System.out.println(content);</span><br><br>            <span class="hljs-type">byte</span>[] contentBytes = content.toString().getBytes(StandardCharsets.UTF_8);<br><br>            System.out.println(contentBytes);<br>            <span class="hljs-comment">// 获取博文</span><br>            <span class="hljs-type">Article</span> <span class="hljs-variable">article</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Article</span>();<br>            article.setId(Long.valueOf(<span class="hljs-string">&quot;7313418992310976549&quot;</span>));<br>            article.setTitle(title.text());<br><br>            <span class="hljs-comment">// 直接保存</span><br>            article.setContent(content.toString());<br><br>            <span class="hljs-comment">// article.setContent(Arrays.toString(contentBytes));</span><br>            <span class="hljs-comment">// article.setContent(content.toString());</span><br>            article.setAuthorId(<span class="hljs-number">0L</span>);<br>            article.setView(<span class="hljs-number">0</span>);<br>            article.setLikes(<span class="hljs-number">0</span>);<br>            article.setComments(<span class="hljs-string">&quot;&quot;</span>);<br>            article.setCollects(<span class="hljs-number">0</span>);<br>            article.setTags(<span class="hljs-string">&quot;&quot;</span>);<br><br>            <span class="hljs-comment">// articleService.save(article);</span><br><br>            <span class="hljs-type">String</span> <span class="hljs-variable">decodedContent</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(contentBytes, StandardCharsets.UTF_8);<br>            System.out.println(<span class="hljs-string">&quot;-------------解码后--------------&quot;</span>);<br>            System.out.println(decodedContent);<br><br>        &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>        &#125;<br>    &#125;<br>article.setContent(contentBytes);<br><br></code></pre></td></tr></table></figure>

<ul>
<li>这里直接保存，会出现字符编码无法识别而转换错误，就是因为保存的数据记录中有 emoji 这样的小图标</li>
<li>这里我也查询了相关文章，解决这个问题，虽然最后没有解决，但仍可做参考：</li>
</ul>
<blockquote>
<p>🔥 推荐阅读：</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_32684617/article/details/93495460">用户昵称含 emoji 表情保存到数据库中报错 SQLException: Incorrect string value: ‘\xF0\x9F\x91\xA7’ for colum n …-CSDN 博客</a></li>
<li><a target="_blank" rel="noopener" href="https://bbs.csdn.net/topics/392268222">java 后台接收获取微信昵称，昵称包含小图标保存到数据库报错-CSDN 社区</a></li>
</ul>
</blockquote>
<figure class="highlight subunit"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs subunit"><span class="hljs-keyword">Error </span>updating database.  Cause: java.sql.SQLException: Incorrect string value: &#x27;\xF0\x9F\x8D\x84 \xE5...&#x27; for column &#x27;content&#x27; at row 1<br></code></pre></td></tr></table></figure>

<ul>
<li>经过测试，转码保存解决报错：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Document</span> <span class="hljs-variable">doc</span> <span class="hljs-operator">=</span> Jsoup.connect(url)<br>                    .userAgent(<span class="hljs-string">&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.81&quot;</span>)<br>                    .get();<br><br><br><span class="hljs-type">Elements</span> <span class="hljs-variable">title</span> <span class="hljs-operator">=</span> doc.select(<span class="hljs-string">&quot;.article-area .article-title&quot;</span>);<br>System.out.println(<span class="hljs-string">&quot;----------------博文标题----------------&quot;</span>);<br>System.out.println(title.text());<br><br><span class="hljs-type">Elements</span> <span class="hljs-variable">content</span> <span class="hljs-operator">=</span> doc.select(<span class="hljs-string">&quot;.article-viewer p&quot;</span>);<br><span class="hljs-comment">// for (Element p : content) &#123;</span><br><span class="hljs-comment">//     p.select(&quot;img&quot;).remove();</span><br><span class="hljs-comment">//     p.select(&quot;a&quot;).remove();</span><br><span class="hljs-comment">// &#125;</span><br><br>System.out.println(<span class="hljs-string">&quot;---------------博文正文------------------&quot;</span>);<br><span class="hljs-comment">// System.out.println(content);</span><br><br><span class="hljs-type">byte</span>[] contentBytes = content.toString().getBytes(StandardCharsets.UTF_8);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">String</span> <span class="hljs-variable">decodedContent</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(contentBytes, StandardCharsets.UTF_8);<br>            System.out.println(<span class="hljs-string">&quot;-------------解码后--------------&quot;</span>);<br>            System.out.println(decodedContent);<br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20231224104703715.png" alt="image-20231224104703715"></p>
<ul>
<li>保存到数据库中的问题解决了，接下来就是保证正确从数据库中拿到数据并解码出原数据：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">String</span> <span class="hljs-variable">contentStr</span> <span class="hljs-operator">=</span> article.getContent();<br><span class="hljs-type">byte</span>[] contentBytes = contentStr.getBytes(StandardCharsets.UTF_8);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 移除两端的方括号</span><br><span class="hljs-type">String</span> <span class="hljs-variable">contentStrWithoutBrackets</span> <span class="hljs-operator">=</span> contentStr.substring(<span class="hljs-number">1</span>, contentStr.length() - <span class="hljs-number">1</span>);<br><span class="hljs-comment">// 使用逗号作为分隔符分割字符串</span><br>String[] byteStrs = contentStrWithoutBrackets.split(<span class="hljs-string">&quot;,&quot;</span>);<br><span class="hljs-comment">// 将字符串数组转换为字节数组</span><br><span class="hljs-type">byte</span>[] contentBytes = <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[byteStrs.length];<br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; byteStrs.length; i++) &#123;<br>    contentBytes[i] = Byte.parseByte(byteStrs[i]);<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li>经过诸多尝试，仍无法正确解码</li>
<li>经过前面的测试发现，转码后保存 byte [] 可以解决编码错误，问题是出在保存数据库时</li>
<li>由于字段 content 为 text（varchar 也可以，可能会出现要保存的数据记录过长而导致溢出，就选择 text 了），所以我们在保存 byte [] 到数据库中时，是先转换成字符串再保存的</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">article.setContent(Arrays.toString(contentBytes));<br></code></pre></td></tr></table></figure>

<ul>
<li>而后才引发了解码失败的问题，因为对 byte [] 直接解码是可以获取原文内容 content 的，但是先转字符串存入，取出时就不好处理了</li>
<li>那就干脆直接保存 byte [] 到数据库中了，改变字段 content 属性为 blob：</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231224104321929.png" alt="image-20231224104321929"></p>
<ul>
<li>改变对应实体类的字段数据类型为 byte []：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 文章内容</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">private</span> <span class="hljs-type">byte</span>[] content;<br></code></pre></td></tr></table></figure>

<ul>
<li>接下来，我们选择直接保存 byte [] 到数据库中即可：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">article.setContent(contentBytes);<br></code></pre></td></tr></table></figure>

<ul>
<li>这里也可以看出，将 byte [] 转字符串数组后保存和直接保存 byte [] 到数据库中的形式是很不一样的（如下图所示）：</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231224104532681.png" alt="image-20231224104532681"></p>
<ul>
<li>接下来，就可以直接从数据库中取出数据并解码了：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Article</span> <span class="hljs-variable">article</span> <span class="hljs-operator">=</span> articleService.getById(<span class="hljs-number">7313418992310976549L</span>);<br><br><span class="hljs-type">Long</span> <span class="hljs-variable">id</span> <span class="hljs-operator">=</span> article.getId();<br><span class="hljs-type">String</span> <span class="hljs-variable">title</span> <span class="hljs-operator">=</span> article.getTitle();<br><span class="hljs-type">byte</span>[] content = article.getContent();<br><br><span class="hljs-type">String</span> <span class="hljs-variable">decodedContent</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(content, StandardCharsets.UTF_8);<br><span class="hljs-type">Integer</span> <span class="hljs-variable">type</span> <span class="hljs-operator">=</span> article.getType();<br><br>System.out.println(id);<br>System.out.println(title);<br>System.out.println(decodedContent);<br>System.out.println(type);<br></code></pre></td></tr></table></figure>

<ul>
<li>解码结果如下：</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20231224104725599.png" alt="image-20231224104725599"></p>
<ul>
<li>至此，我们成功解决了如何正确保存数据记录到数据库中的问题，并成功解决了编码问题</li>
</ul>
<h3 id="汉字转拼音"><a href="#汉字转拼音" class="headerlink" title="汉字转拼音"></a>汉字转拼音</h3><ul>
<li>开发 API 接口开放平台的<code>获取随机壁纸接口</code>服务：<a target="_blank" rel="noopener" href="https://www.vcg.com/">视觉中国——全球领先的视觉素材数字版权库和交易平台 (vcg.com)</a></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Random</span> <span class="hljs-variable">random</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Random</span>();<br><span class="hljs-type">int</span> <span class="hljs-variable">randomPage</span> <span class="hljs-operator">=</span> random.nextInt(<span class="hljs-number">5</span>) + <span class="hljs-number">1</span>;<br><span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> String.format(<span class="hljs-string">&quot;https://www.vcg.com/creative-image/%s/?page=%d&quot;</span>, category, randomPage);<br><span class="hljs-type">String</span> <span class="hljs-variable">userAgent</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.81&quot;</span>;<br></code></pre></td></tr></table></figure>

<ul>
<li>拼接 <code>category </code>，只要是中文字符就不定时出现报错，尝试将中文转为拼音：</li>
<li>导入依赖：<strong>（2024/01/18 晚）</strong></li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.belerweb<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>pinyin4j<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.5.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span> <span class="hljs-comment">&lt;!-- 使用时检查是否有更新的版本 --&gt;</span><br>      <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<ul>
<li>开始转换：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 带声调</span><br><span class="hljs-type">StringBuilder</span> <span class="hljs-variable">output</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>();<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">char</span> c : name.toCharArray()) &#123;<br>        <span class="hljs-keyword">try</span> &#123;<br>            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);<br>            <span class="hljs-keyword">if</span> (pinyinArray != <span class="hljs-literal">null</span> &amp;&amp; pinyinArray.length &gt; <span class="hljs-number">0</span>) &#123;<br>                output.append(pinyinArray[<span class="hljs-number">0</span>]).append(<span class="hljs-string">&quot; &quot;</span>);<br>            &#125; <span class="hljs-keyword">else</span> &#123;<br>                output.append(c).append(<span class="hljs-string">&quot; &quot;</span>);<br>            &#125;<br>        &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>            output.append(c).append(<span class="hljs-string">&quot; &quot;</span>);<br>        &#125;<br>    &#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 不带声调</span><br>net.sourceforge.pinyin4j.format.<span class="hljs-type">HanyuPinyinOutputFormat</span> <span class="hljs-variable">format</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">net</span>.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat();<br>       format.setToneType(net.sourceforge.pinyin4j.format.HanyuPinyinToneType.WITHOUT_TONE);<br><br>       <span class="hljs-type">StringBuilder</span> <span class="hljs-variable">output</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringBuilder</span>();<br>       <span class="hljs-keyword">for</span> (<span class="hljs-type">char</span> c : name.toCharArray()) &#123;<br>           <span class="hljs-keyword">try</span> &#123;<br>               String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c, format);<br>               <span class="hljs-keyword">if</span> (pinyinArray != <span class="hljs-literal">null</span> &amp;&amp; pinyinArray.length &gt; <span class="hljs-number">0</span>) &#123;<br>                   output.append(pinyinArray[<span class="hljs-number">0</span>]).append(<span class="hljs-string">&quot;&quot;</span>);<br>               &#125; <span class="hljs-keyword">else</span> &#123;<br>                   output.append(c);<br>               &#125;<br>           &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>               output.append(c).append(<span class="hljs-string">&quot;&quot;</span>);<br>           &#125;<br>       &#125;<br></code></pre></td></tr></table></figure>

<h3 id="无法加载-Spring-的配置文件"><a href="#无法加载-Spring-的配置文件" class="headerlink" title="无法加载 Spring 的配置文件"></a>无法加载 Spring 的配置文件</h3><p>启动测试类，发现这样的报错：<strong>（2024/02/13 早）</strong></p>
<p><img src="http://blog.memory-life.xyz/image-20240213094455214.png" alt="image-20240213094455214"></p>
<p>在 SpringBoot 中报错 java.lang.IllegalStateException: Failed to load ApplicationContext，无法加载 Spring 的配置文件</p>
<p>出现这种问题，无外于这几个方面：jar 包过于老旧、@Bean 注入有分歧、配置文件中的数据库连接失败、未扫描到 Mapper</p>
<p>1、jar 包有问题或者过老，换最新的试试。<br>2、 内部 Bean 配置有歧义，Spring 自身无法分辨<br>3、缺少某个依赖、或属性的配置<br>4、引用 外部属性文件的情况下，属性文件内有错误，无法加载。比如属性文件配置的数据库连接 有问题<br>5、在使用到 applicationContext 的 地方引用的路径不正确。</p>
<p>🍖 推荐阅读： <a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_43235147/article/details/105781532">java.lang.IllegalStateException: Failed to load ApplicationContext-CSDN 博客</a></p>
<p>我的问题解决了，确实是配置文件中数据库连接有问题。我这个项目配置了本地 Elasticsearch 的，启动 本地 ES 就好了</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># Elasticsearch 配置</span><br><span class="hljs-attr">elasticsearch:</span><br>  <span class="hljs-attr">uris:</span> <span class="hljs-string">http://localhost:9200</span><br>  <span class="hljs-attr">username:</span> <span class="hljs-string">root</span><br>  <span class="hljs-attr">password:</span> <span class="hljs-string">******</span><br></code></pre></td></tr></table></figure>

<h3 id="ControllerAdvice-拦截"><a href="#ControllerAdvice-拦截" class="headerlink" title="@ControllerAdvice 拦截"></a>@ControllerAdvice 拦截</h3><blockquote>
<p>2024 年 4 月 25 日</p>
</blockquote>
<p>🍖 推荐阅读：<a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_43575801/article/details/128891997">@ControllerAdvice 的介绍及三种用法(转载)-CSDN 博客</a></p>
<p>在看单位的后端开发代码模板，看到了这个类：</p>
<p>这段代码是一个名为 ApiResultHandler 的类，它实现了 Spring 框架中的 ResponseBodyAdvice 接口。这个类的主要作用是对 RestController 的接口方法进行拦截，对返回的结果进行处理。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ControllerAdvice(annotations = &#123;RestController.class&#125;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ApiResultHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">ResponseBodyAdvice</span> &#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Class[] annos = &#123;<br>            RequestMapping.class,<br>            GetMapping.class,<br>            PostMapping.class,<br>            DeleteMapping.class,<br>            PutMapping.class<br>    &#125;;<br><br>...............................<br>&#125;<br></code></pre></td></tr></table></figure>

<p>首先，定义了一个静态数组 annos，包含了 RequestMapping、GetMapping、PostMapping、DeleteMapping 和 PutMapping 这五个注解，用于判断一个方法是否使用了这些注解。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 对所有RestController的接口方法进行拦截</span><br><span class="hljs-comment">    */</span><br>   <span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">supports</span><span class="hljs-params">(MethodParameter returnType, Class converterType)</span> &#123;<br>       <span class="hljs-type">AnnotatedElement</span> <span class="hljs-variable">element</span> <span class="hljs-operator">=</span> returnType.getAnnotatedElement();<br>       <span class="hljs-keyword">return</span> Arrays.stream(annos).anyMatch(anno -&gt; anno.isAnnotation() &amp;&amp; element.isAnnotationPresent(anno));<br>&#125;<br></code></pre></td></tr></table></figure>

<p>重写了 supports 方法，用于判断当前拦截的方法是否使用了上述五个注解之一。如果使用了，返回 true，表示需要拦截；否则返回 false，表示不需要拦截。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br>   <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">beforeBodyWrite</span><span class="hljs-params">(<span class="hljs-meta">@Nullable</span> Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response)</span> &#123;<br><br>	<span class="hljs-keyword">if</span>(body <span class="hljs-keyword">instanceof</span> RestfulResponse)&#123;<br>		<span class="hljs-keyword">return</span> body;<br>	&#125;<br><br>	<span class="hljs-comment">//feign内部请求，不转格式</span><br>	<span class="hljs-type">HttpHeaders</span> <span class="hljs-variable">headers</span> <span class="hljs-operator">=</span> request.getHeaders();<br>	List&lt;String&gt; list = headers.get(FeginClientConfig.KEEP_ORIGINAL);<br>	<span class="hljs-keyword">if</span>(list != <span class="hljs-literal">null</span> &amp;&amp; list.contains(FeginClientConfig.KEEP_ORIGINAL)) &#123;<br>		<span class="hljs-keyword">return</span> body;<br>	&#125;<br><br>	<span class="hljs-keyword">if</span>(body <span class="hljs-keyword">instanceof</span> ResultCode) &#123;<br>		<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RestfulResponse</span>(((ResultCode) body).getCode(), ((ResultCode) body).getDesc());<br>	&#125;<br><br>   	<span class="hljs-type">AnnotatedElement</span> <span class="hljs-variable">element</span> <span class="hljs-operator">=</span> returnType.getAnnotatedElement();<br>   	<span class="hljs-keyword">if</span>(element.isAnnotationPresent(SuccessfulMessage.class)) &#123;<br>   		<span class="hljs-type">boolean</span> <span class="hljs-variable">ignore</span> <span class="hljs-operator">=</span> element.getAnnotation(SuccessfulMessage.class).ignore();<br>   		<span class="hljs-keyword">if</span>(ignore) &#123;<br>   			<span class="hljs-keyword">return</span> body;<br>   		&#125;<br>   	&#125;<br><br>   	<span class="hljs-type">String</span> <span class="hljs-variable">msg</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br><br>   	<span class="hljs-comment">//1. 配置文件优先级最高</span><br>   	<span class="hljs-comment">//2. 方法上的 SuccessfulMessage</span><br>   	<span class="hljs-comment">//3. 默认</span><br>   	<span class="hljs-comment">//		RequestMapping 成功</span><br>   	<span class="hljs-comment">//		GetMapping 查询成功</span><br>   	<span class="hljs-comment">//		PostMapping 添加成功</span><br>   	<span class="hljs-comment">//		DeleteMapping 删除成功</span><br>   	<span class="hljs-comment">//		PutMapping 修改成功</span><br><br>   	<span class="hljs-keyword">if</span>(element.isAnnotationPresent(SuccessfulMessage.class)) &#123;<br>   		<span class="hljs-comment">/*String value = element.getAnnotation(SuccessfulMessage.class).value();</span><br><span class="hljs-comment">   		//解析EL $&#123;&#125;</span><br><span class="hljs-comment">   		if(value != null) &#123;</span><br><span class="hljs-comment">   			value = value.trim();</span><br><span class="hljs-comment">   			if(value.startsWith(&quot;$&#123;&quot;) &amp;&amp; value.endsWith(&quot;&#125;&quot;)) &#123;</span><br><span class="hljs-comment">   				value = value.substring(2, value.length()-1);</span><br><span class="hljs-comment">   				String[] separator = StringUtils.splitByWholeSeparator(value, &quot;:&quot;);</span><br><span class="hljs-comment">   				String key = separator[0];</span><br><span class="hljs-comment">   				String defaultValue = separator.length &gt; 1 ? separator[1] : null;</span><br><span class="hljs-comment">   				//查询配置文件</span><br><span class="hljs-comment">   				msg = env.getProperty(key, defaultValue);</span><br><span class="hljs-comment">   			&#125; else &#123;</span><br><span class="hljs-comment">   				msg = value;</span><br><span class="hljs-comment">   			&#125;</span><br><span class="hljs-comment">   		&#125;*/</span><br>		msg = element.getAnnotation(SuccessfulMessage.class).value();<br>   	&#125;<br><br>   	<span class="hljs-keyword">if</span>(msg == <span class="hljs-literal">null</span>) &#123;<br>   		<span class="hljs-keyword">if</span>(element.isAnnotationPresent(GetMapping.class)) &#123;<br>   			msg = <span class="hljs-string">&quot;操作成功&quot;</span>;<br>   		&#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(element.isAnnotationPresent(PostMapping.class)) &#123;<br>   			msg = <span class="hljs-string">&quot;操作成功&quot;</span>;<br>   		&#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(element.isAnnotationPresent(PutMapping.class)) &#123;<br>   			msg = <span class="hljs-string">&quot;修改成功&quot;</span>;<br>   		&#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(element.isAnnotationPresent(DeleteMapping.class)) &#123;<br>   			msg = <span class="hljs-string">&quot;删除成功&quot;</span>;<br>   		&#125; <span class="hljs-keyword">else</span> &#123;<br>   			msg = <span class="hljs-string">&quot;请求成功&quot;</span>;<br>   		&#125;<br>   	&#125;<br>       <span class="hljs-keyword">return</span> RestfulResponse.success(msg, body);<br>   &#125;<br></code></pre></td></tr></table></figure>

<p>重写了 beforeBodyWrite 方法，这个方法会在 Controller 方法执行后，返回结果之前被调用。在这个方法中，对返回的结果进行了处理：</p>
<ul>
<li>如果返回的结果已经是 RestfulResponse 类型，直接返回；</li>
<li>如果请求头中包含 FeginClientConfig.KEEP_ORIGINAL，表示是内部请求，不进行格式转换，直接返回；</li>
<li>如果返回的结果是 ResultCode 类型，将其转换为 RestfulResponse 类型并返回；</li>
<li>如果方法上有 SuccessfulMessage 注解，根据注解的属性决定是否忽略该次拦截；</li>
<li>根据方法上的注解（如 GetMapping、PostMapping 等）设置默认的成功消息；</li>
<li>最后，将成功消息和原始结果封装成 RestfulResponse 对象并返回。</li>
</ul>
<h3 id="定时任务实现"><a href="#定时任务实现" class="headerlink" title="定时任务实现"></a>定时任务实现</h3><ul>
<li><p>思来想去，这个栏目还是写在这儿比较合适，介绍一下在 Java 中，如何写一个<strong>定时任务</strong></p>
</li>
<li><h5 id="推荐阅读：我终于会写-Java-的定时任务了！-掘金-juejin-cn"><a href="#推荐阅读：我终于会写-Java-的定时任务了！-掘金-juejin-cn" class="headerlink" title="推荐阅读：我终于会写 Java 的定时任务了！ - 掘金 (juejin.cn)"></a>推荐阅读：<a target="_blank" rel="noopener" href="https://juejin.cn/post/7158851405531316254">我终于会写 Java 的定时任务了！ - 掘金 (juejin.cn)</a></h5></li>
</ul>
<h4 id="纯手写单线程循环"><a href="#纯手写单线程循环" class="headerlink" title="纯手写单线程循环"></a>纯手写单线程循环</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">timer1</span><span class="hljs-params">()</span> &#123;<br>     <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(() -&gt; &#123;<br>         <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) &#123;<br>             System.out.println(<span class="hljs-string">&quot;定时任务A 当前时间: &quot;</span> + LocalDateTime.now());<br>             <span class="hljs-keyword">try</span> &#123;<br>                 Thread.sleep(<span class="hljs-number">1000</span>);<br>             &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                 <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>             &#125;<br>         &#125;<br>     &#125;).start();<br> &#125;<br></code></pre></td></tr></table></figure>

<h4 id="Timer-和他的小伙伴"><a href="#Timer-和他的小伙伴" class="headerlink" title="Timer 和他的小伙伴"></a>Timer 和他的小伙伴</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">timer2</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-comment">// 单线程</span><br>        <span class="hljs-type">Timer</span> <span class="hljs-variable">timer</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Timer</span>();<br>        System.out.println(<span class="hljs-string">&quot;1秒后执行任务A，A完成后，等待1秒开始定时执行任务B，当前时间: &quot;</span> + LocalDateTime.now());<br>        <span class="hljs-comment">// 1秒后执行</span><br>        timer.schedule(<span class="hljs-keyword">new</span> <span class="hljs-title class_">TimerTask</span>() &#123;<br>            <span class="hljs-meta">@Override</span><br>            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> &#123;<br>                System.out.println(<span class="hljs-string">&quot;定时任务A 当前时间: &quot;</span> + LocalDateTime.now());<br>            &#125;<br>        &#125;, <span class="hljs-number">1000</span>); <span class="hljs-comment">// 这里 1000，就是代表延迟 1000 毫秒后再执行</span><br><br>        <span class="hljs-comment">// 每隔2秒执行一次这个任务</span><br>        timer.schedule(<span class="hljs-keyword">new</span> <span class="hljs-title class_">TimerTask</span>() &#123;<br>            <span class="hljs-meta">@Override</span><br>            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> &#123;<br>                System.out.println(<span class="hljs-string">&quot;定时任务B 当前时间: &quot;</span> + LocalDateTime.now());<br>            &#125;<br>        &#125;, <span class="hljs-number">1000</span>, <span class="hljs-number">2000</span>); <span class="hljs-comment">// 1000 同理，2000 即执行完本次任务后，隔 2000 毫秒后再一次执行，达到定时任务的效果</span><br>    &#125;<br></code></pre></td></tr></table></figure>

<h4 id="ScheduledExecutorService"><a href="#ScheduledExecutorService" class="headerlink" title="ScheduledExecutorService"></a>ScheduledExecutorService</h4><ul>
<li><h5 id="定时执行"><a href="#定时执行" class="headerlink" title="定时执行"></a>定时执行</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">timer4</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-type">ScheduledExecutorService</span> <span class="hljs-variable">scheduledExecutorService</span> <span class="hljs-operator">=</span> Executors.newSingleThreadScheduledExecutor();<br>        System.out.println(<span class="hljs-string">&quot;2秒后开始执行任务，此刻时间---&quot;</span> + LocalDateTime.now());<br>        <span class="hljs-comment">// 固定频率（每隔5秒）开始执行一个任务</span><br>        scheduledExecutorService.scheduleAtFixedRate(() -&gt; &#123;<br>            System.out.println(<span class="hljs-string">&quot;任务开始---&quot;</span> + LocalDateTime.now());<br>            <span class="hljs-keyword">try</span> &#123;<br>                Thread.sleep(<span class="hljs-number">2000</span>);<br>            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                e.printStackTrace();<br>            &#125;<br>            System.out.println(<span class="hljs-string">&quot;任务结束---&quot;</span> + LocalDateTime.now());<br>        &#125;, <span class="hljs-number">2000</span>, <span class="hljs-number">5000</span>, TimeUnit.MILLISECONDS);<br>    &#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="延时执行"><a href="#延时执行" class="headerlink" title="延时执行"></a>延时执行</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">timer5</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-type">ScheduledExecutorService</span> <span class="hljs-variable">scheduledExecutorService</span> <span class="hljs-operator">=</span> Executors.newSingleThreadScheduledExecutor();<br>        System.out.println(<span class="hljs-string">&quot;2秒后开始执行任务，此刻时间---&quot;</span> + LocalDateTime.now());<br>        <span class="hljs-comment">// 任务完成后间隔4秒开始执行下一次任务</span><br>        scheduledExecutorService.scheduleWithFixedDelay(() -&gt; &#123;<br>            System.out.println(<span class="hljs-string">&quot;任务开始---&quot;</span> + LocalDateTime.now());<br>            <span class="hljs-keyword">try</span> &#123;<br>                Thread.sleep(<span class="hljs-number">2000</span>);<br>            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                e.printStackTrace();<br>            &#125;<br>            System.out.println(<span class="hljs-string">&quot;任务结束---&quot;</span> + LocalDateTime.now());<br>        &#125;, <span class="hljs-number">2000</span>, <span class="hljs-number">4000</span>, TimeUnit.MILLISECONDS);<br>    &#125;<br></code></pre></td></tr></table></figure>

<h4 id="DelayQueue-延迟任务"><a href="#DelayQueue-延迟任务" class="headerlink" title="DelayQueue 延迟任务"></a>DelayQueue 延迟任务</h4><ul>
<li><h5 id="DelayQueue-是-JDK-提供的-api，是一个延迟队列"><a href="#DelayQueue-是-JDK-提供的-api，是一个延迟队列" class="headerlink" title="DelayQueue 是 JDK 提供的 api，是一个延迟队列"></a>DelayQueue 是 JDK 提供的 api，是一个延迟队列</h5></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DelayQueueDemo</span> &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        DelayQueue&lt;SanYouTask&gt; sanYouTaskDelayQueue = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DelayQueue</span>&lt;&gt;();<br><br>        <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(() -&gt; &#123;<br>            <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) &#123;<br>                <span class="hljs-keyword">try</span> &#123;<br>                    <span class="hljs-type">SanYouTask</span> <span class="hljs-variable">sanYouTask</span> <span class="hljs-operator">=</span> sanYouTaskDelayQueue.take();<br>                    log.info(<span class="hljs-string">&quot;获取到延迟任务:&#123;&#125;&quot;</span>, sanYouTask.getTaskContent());<br>                &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>                &#125;<br>            &#125;<br>        &#125;).start();<br><br>        log.info(<span class="hljs-string">&quot;提交延迟任务&quot;</span>);<br>        sanYouTaskDelayQueue.offer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">SanYouTask</span>(<span class="hljs-string">&quot;三友的java日记5s&quot;</span>, <span class="hljs-number">5L</span>));<br>        sanYouTaskDelayQueue.offer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">SanYouTask</span>(<span class="hljs-string">&quot;三友的java日记3s&quot;</span>, <span class="hljs-number">3L</span>));<br>        sanYouTaskDelayQueue.offer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">SanYouTask</span>(<span class="hljs-string">&quot;三友的java日记8s&quot;</span>, <span class="hljs-number">8L</span>));<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SanYouTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Delayed</span> &#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String taskContent;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Long triggerTime;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">SanYouTask</span><span class="hljs-params">(String taskContent, Long delayTime)</span> &#123;<br>        <span class="hljs-built_in">this</span>.taskContent = taskContent;<br>        <span class="hljs-built_in">this</span>.triggerTime = System.currentTimeMillis() + delayTime * <span class="hljs-number">1000</span>;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">long</span> <span class="hljs-title function_">getDelay</span><span class="hljs-params">(TimeUnit unit)</span> &#123;<br>        <span class="hljs-keyword">return</span> unit.convert(triggerTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">compareTo</span><span class="hljs-params">(Delayed o)</span> &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.triggerTime.compareTo(((SanYouTask) o).triggerTime);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><code>getDelay</code>方法返回<strong>这个任务还剩多久时间可以执行</strong>，小于 0 的时候说明可以这个延迟任务到了执行的时间了。</li>
<li><code>compareTo</code>这个是<strong>对任务排序</strong>的，保证最先到延迟时间的任务排到队列的头。<ul>
<li><strong>taskContent</strong>：延迟任务的具体的内容</li>
<li><strong>delayTime</strong>：延迟时间，秒为单位</li>
</ul>
</li>
</ul>
<h5 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h5><blockquote>
<p>🍻 <code>offer</code>方法在提交任务的时候，会通过根据<code>compareTo</code>的实现对任务进行排序，将最先需要被执行的任务放到队列头。</p>
<p>🍛<code>take</code>方法获取任务的时候，会拿到队列头部的元素，也就是队列中最早需要被执行的任务，通过 getDelay 返回值判断任务是否需要被立刻执行，如果需要的话，就返回任务，如果不需要就会等待这个任务到延迟时间的剩余时间，当时间到了就会将任务返回。</p>
</blockquote>
<ul>
<li>效果如下：</li>
</ul>
<h4 id="Spring-提供定时任务"><a href="#Spring-提供定时任务" class="headerlink" title="Spring 提供定时任务"></a>Spring 提供定时任务</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> 邓哈哈</span><br><span class="hljs-comment"> * 2023/8/29 11:50</span><br><span class="hljs-comment"> * Function:</span><br><span class="hljs-comment"> * Version 1.0</span><br><span class="hljs-comment"> */</span><br><br><span class="hljs-meta">@EnableScheduling</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Timer</span> &#123;<br>    <span class="hljs-meta">@Scheduled(cron = &quot;*/2 * * * * *&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">timer</span><span class="hljs-params">()</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;哈哈哈哈&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ul>
<li><h5 id="如果有多个定时任务类，可以考虑把-EnableScheduling-注解添加在启动类上"><a href="#如果有多个定时任务类，可以考虑把-EnableScheduling-注解添加在启动类上" class="headerlink" title="如果有多个定时任务类，可以考虑把@EnableScheduling 注解添加在启动类上"></a>如果有多个定时任务类，可以考虑把@EnableScheduling 注解添加在启动类上</h5></li>
</ul>
<h4 id="Cron-表达式"><a href="#Cron-表达式" class="headerlink" title="Cron 表达式"></a>Cron 表达式</h4><ul>
<li><h5 id="在线生成工具：在线-Cron-表达式生成器-qqe2-com"><a href="#在线生成工具：在线-Cron-表达式生成器-qqe2-com" class="headerlink" title="在线生成工具：在线 Cron 表达式生成器 (qqe2.com)"></a>在线生成工具：<a target="_blank" rel="noopener" href="https://cron.qqe2.com/">在线 Cron 表达式生成器 (qqe2.com)</a></h5></li>
<li><h5 id="简单地介绍下如何使用吧：（2023-08-25-早）"><a href="#简单地介绍下如何使用吧：（2023-08-25-早）" class="headerlink" title="简单地介绍下如何使用吧：（2023/08/25 早）"></a>简单地介绍下如何使用吧：（2023/08/25 早）</h5></li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20230829122937976.png" alt="image-20230829122937976"></p>
<p><img src="http://blog.memory-life.xyz/image-20230829123041111.png" alt="image-20230829123041111"></p>
<p><img src="http://blog.memory-life.xyz/image-20230829123201856.png" alt="image-20230829123201856"></p>
<h3 id="代码合并"><a href="#代码合并" class="headerlink" title="代码合并"></a>代码合并</h3><blockquote>
<p>2024 年 5 月 22 日</p>
</blockquote>
<p>今天学习：在团队协同开发工作中，如何使用 IDEA 整合的 git 功能实现<strong>本地代码提交至远程仓库</strong>。</p>
<p>选择要提交的文件：</p>
<p><img src="http://blog.memory-life.xyz/image-20240522114357514.png" alt="image-20240522114357514"></p>
<p>编写完毕 commit 信息后，选择好分支，拉取远程仓库最新代码：</p>
<p><img src="http://blog.memory-life.xyz/image-20240522114503426.png" alt="image-20240522114503426"></p>
<p>直接 push 即可：</p>
<p><img src="http://blog.memory-life.xyz/image-20240522114610086.png" alt="image-20240522114610086"></p>
<h3 id="IDEA-连接-MySQL-失败"><a href="#IDEA-连接-MySQL-失败" class="headerlink" title="IDEA 连接 MySQL 失败"></a>IDEA 连接 MySQL 失败</h3><blockquote>
<p>2024 年 5 月 29 日</p>
</blockquote>
<p>经过排查，发现是从 maven 下载 MySQL 驱动失败了，</p>
<p><img src="http://blog.memory-life.xyz/image-20240529080517771.png" alt="image-20240529080517771"></p>
<p>只好是手动指定本地 MySQL 驱动路径，直接在本机的 maven 目录下找就可以，我的在这：</p>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">D</span>:\softWare\Maven\apache-maven-<span class="hljs-number">3</span>.<span class="hljs-number">6</span>.<span class="hljs-number">1</span>\mvn_resp\mysql<br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20240529082955156.png" alt="image-20240529082955156"></p>
<p>设置下时区，有两种设置方法：</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_44190665/article/details/115601295">解决：Server returns invalid timezone. Need to set ‘serverTimezone‘ property._server returns invalid timezone. need to set ‘serv-CSDN 博客</a></p>
<p><img src="http://blog.memory-life.xyz/image-20240529081423926.png" alt="image-20240529081423926"></p>
<p>确实解决了问题：</p>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">DBMS</span>: MySQL (ver. <span class="hljs-number">5</span>.<span class="hljs-number">7</span>.<span class="hljs-number">19</span>-log)<br><span class="hljs-attribute">Case</span> sensitivity: plain=lower, delimited=lower<br><span class="hljs-attribute">Driver</span>: MySQL Connector/J (ver. mysql-connector-java-<span class="hljs-number">8</span>.<span class="hljs-number">0</span>.<span class="hljs-number">21</span> (Revision: <span class="hljs-number">33</span>f65445a1bcc544eb0120491926484da168f199), JDBC4.<span class="hljs-number">2</span>)<br><br><span class="hljs-attribute">Ping</span>: <span class="hljs-number">33</span> ms<br><span class="hljs-attribute">SSL</span>: no<br></code></pre></td></tr></table></figure>

<p>但是连接上了 MySQL，却不显示数据库，显示为空是什么情况。</p>
<p>参考：<a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_62124267/article/details/132100787">【IDEA 连接 MySQL 服务后不显示本地数据库？——这样做！】_idea 的数据库内容没办法显示-CSDN 博客</a></p>
<p><img src="http://blog.memory-life.xyz/image-20240529083348441.png" alt="image-20240529083348441"></p>
<p>那是因为我连接数据库时没有选定具体数据库，默认只有系统自带的。</p>
<p>可以如上图一样勾选，也可以在连接数据库时的配置中填充，就不会出现这样的问题：</p>
<p><img src="http://blog.memory-life.xyz/image-20240529083514118.png" alt="image-20240529083514118"></p>
<h3 id="Swagger-文档"><a href="#Swagger-文档" class="headerlink" title="Swagger 文档"></a>Swagger 文档</h3><blockquote>
<p>2024 年 5 月 29 日</p>
</blockquote>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">server:</span><br>    <span class="hljs-attr">address:</span> <span class="hljs-string">localhost</span><br>    <span class="hljs-attr">port:</span> <span class="hljs-number">8101</span><br>    <span class="hljs-attr">servlet:</span><br>      <span class="hljs-attr">context-path:</span> <span class="hljs-string">/api</span><br></code></pre></td></tr></table></figure>

<p>在项目启动入口如此编写：统计启动时间，读取配置获取 <strong>ip、端口号和路径</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootApplication(exclude = &#123;RedisAutoConfiguration.class&#125;)</span><br><span class="hljs-meta">@MapperScan(&quot;com.yupi.springbootinit.mapper&quot;)</span><br><span class="hljs-meta">@EnableScheduling</span><br><span class="hljs-meta">@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MainApplication</span> &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        <span class="hljs-type">ConfigurableApplicationContext</span> <span class="hljs-variable">ctx</span> <span class="hljs-operator">=</span> SpringApplication.run(MainApplication.class, args);<br>        <span class="hljs-type">long</span> <span class="hljs-variable">start</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br><br><br>        <span class="hljs-type">long</span> <span class="hljs-variable">time</span> <span class="hljs-operator">=</span> (System.currentTimeMillis() - start) / <span class="hljs-number">1000</span>;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">info</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;启动完成，耗时%d秒，swagger访问链接：http://%s:%s%s/doc.html&quot;</span>;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">address</span> <span class="hljs-operator">=</span> ctx.getEnvironment().getProperty(<span class="hljs-string">&quot;server.address&quot;</span>);<br>        <span class="hljs-type">String</span> <span class="hljs-variable">port</span> <span class="hljs-operator">=</span> ctx.getEnvironment().getProperty(<span class="hljs-string">&quot;server.port&quot;</span>);<br>        <span class="hljs-type">String</span> <span class="hljs-variable">path</span> <span class="hljs-operator">=</span> ctx.getEnvironment().getProperty(<span class="hljs-string">&quot;server.servlet.context-path&quot;</span>);<br>        log.info(String.format(info, time, address, port, path));<br>    &#125;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20240529090430264.png" alt="image-20240529090430264"></p>
<p>这样启动项目后，很便捷地就能访问到 swagger 接口文档了：</p>
<p><img src="http://blog.memory-life.xyz/image-20240529090523288.png" alt="image-20240529090523288"></p>
<blockquote>
<p>2024 年 8 月 31 日</p>
</blockquote>
<p><img src="C:/Users/Lenovo/AppData/Roaming/Typora/typora-user-images/image-20240831160000499.png" alt="image-20240831160000499"></p>
<p>这个错误是由于 Spring Data Redis 在尝试从 Redis 中反序列化存储的对象时遇到了问题。具体来说，错误提示中提到了<code>java.lang.ClassNotFoundException: com.memory.spring_0829.model.entity.User</code>，这意味着在尝试加载并实例化<code>User</code>类时，JVM 在类路径中找不到这个类。</p>
<p>这个问题通常有几个可能的原因：</p>
<ol>
<li><strong>类路径不一致</strong>：存储到 Redis 中的对象是在一个类路径配置下序列化的，但是在尝试反序列化时，应用的类路径配置可能已经改变，导致 JVM 无法找到<code>User</code>类。</li>
<li><strong>部署问题</strong>：如果你是在开发环境和生产环境之间迁移应用，确保所有必要的类和资源都正确地打包并部署到了生产环境。</li>
<li><strong>序列化/反序列化配置</strong>：你可能在序列化对象时使用了 Java 的默认序列化机制（如<code>JdkSerializationRedisSerializer</code>），但是<code>User</code>类可能不再存在于反序列化时的环境中，或者类的定义已经改变（比如类的包名或路径被修改了）。</li>
</ol>
<h3 id="YAML-格式有误"><a href="#YAML-格式有误" class="headerlink" title="YAML 格式有误"></a>YAML 格式有误</h3><blockquote>
<p>2024 年 6 月 1 日</p>
</blockquote>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20240603100138051.png" alt="image-20240603100138051"></p>
<p><img src="http://blog.memory-life.xyz/image-20240607082241950.png" alt="image-20240607082241950"></p>
<p>今天上午遇到的报错：</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs stylus"><span class="hljs-number">09</span>:<span class="hljs-number">56</span>:<span class="hljs-number">32.110</span> <span class="hljs-selector-attr">[restartedMain]</span> ERROR org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.SpringApplication</span> - Application run failed<br>org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.error</span><span class="hljs-selector-class">.YAMLException</span>: java<span class="hljs-selector-class">.nio</span><span class="hljs-selector-class">.charset</span><span class="hljs-selector-class">.MalformedInputException</span>: Input length = <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure>

<p>这个错误日志显示你的 Spring Boot 应用程序在尝试启动时遇到了问题，特别是在解析 YAML 配置文件时。错误 <code>org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length = 1</code> 通常意味着 YAML 解析器在读取配置文件时遇到了字符编码问题。</p>
<p>具体来说，<code>MalformedInputException</code> 指的是输入数据（在这里是 YAML 配置文件）与期望的字符编码格式不匹配。这可能是因为：</p>
<ol>
<li>YAML 文件可能包含无法用当前字符集（通常是 UTF-8）解码的字符。</li>
<li>YAML 文件可能已被保存为不同的字符编码格式（如 ISO-8859-1 或其他）。</li>
<li>在读取或处理 YAML 文件时，可能指定了错误的字符编码。</li>
</ol>
<p>最后发现是因为不小心缩进了一下 application.yaml 配置文件，改回来就好了。</p>
<h3 id="Spring-Cloud-依赖版本"><a href="#Spring-Cloud-依赖版本" class="headerlink" title="Spring Cloud 依赖版本"></a>Spring Cloud 依赖版本</h3><blockquote>
<p>2024 年 6 月 7 日</p>
</blockquote>
<p><img src="http://blog.memory-life.xyz/image-20240606160142339.png"></p>
<p>🍖 推荐阅读：<a target="_blank" rel="noopener" href="https://sca.aliyun.com/docs/2021/overview/version-explain/?spm=5176.29160081.0.0.74801a157QLVDg">版本发布说明 | https://sca.aliyun.com</a></p>
<h3 id="Nacos-未配置"><a href="#Nacos-未配置" class="headerlink" title="Nacos 未配置"></a>Nacos 未配置</h3><blockquote>
<p>2024 年 6 月 7 日</p>
</blockquote>
<p><img src="http://blog.memory-life.xyz/image-20240607082241950.png" alt="image-20240607082241950"></p>
<p>这个问题真的恶心，一直提醒我 yaml 配置文件有误，也不知道是格式有误还是少配置了些东西：</p>
<p><img src="http://blog.memory-life.xyz/image-20240607083651186.png" alt="image-20240607083651186"></p>
<p>我把配置文件删得只剩端口号了，它才提示我检测到 nacos 依赖，却没有扫描到相关配置。</p>
<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs routeros"><span class="hljs-built_in">Add</span> a spring.config.<span class="hljs-attribute">import</span>=nacos: property <span class="hljs-keyword">to</span> your configuration.<br>	<span class="hljs-keyword">If</span> configuration is <span class="hljs-keyword">not</span> required <span class="hljs-built_in">add</span> spring.config.<span class="hljs-attribute">import</span>=optional:nacos: instead.<br>	<span class="hljs-keyword">To</span> <span class="hljs-built_in">disable</span> this check, <span class="hljs-built_in">set</span> spring.cloud.nacos.config.import-check.<span class="hljs-attribute">enabled</span>=<span class="hljs-literal">false</span>.<br></code></pre></td></tr></table></figure>

<p>要么配置不检测 nacos 相关配置，要么显示配置 nacos。</p>
<p>妈的就不能直接提示么。</p>
<p>还有一个问题，坑死我了。</p>
<p>yaml 配置文件里最好不要写中文注释，否则也会不停地提示你配置文件格式有误，你还找不到哪里有问题。</p>
<h3 id="找不着主类"><a href="#找不着主类" class="headerlink" title="找不着主类"></a>找不着主类</h3><blockquote>
<p>2024 年 6 月 7 日</p>
</blockquote>
<p><img src="http://blog.memory-life.xyz/image-20240607083156819.png" alt="image-20240607083156819"></p>
<p>怪了，我好像也没怎么改配置吧，突然就行了：</p>
<p><img src="C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20240607110520450.png" alt="image-20240607110520450"></p>
<h3 id="依赖配置心得"><a href="#依赖配置心得" class="headerlink" title="依赖配置心得"></a>依赖配置心得</h3><blockquote>
<p>2024 年 6 月 7 日</p>
</blockquote>
<ol>
<li>有些公共依赖不要搞在父项目中，比如 Swagger、MybatisPlus 这些</li>
<li>微服务项目一定要注意版本兼容，Spring Boot，Spring Cloud Alibaba，Spring Cloud 这些。</li>
</ol>
<h3 id="日志输出"><a href="#日志输出" class="headerlink" title="日志输出"></a>日志输出</h3><blockquote>
<p>2024 年 6 月 7 日</p>
</blockquote>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">logging:</span><br>  <span class="hljs-attr">level:</span><br>    <span class="hljs-attr">root:</span> <span class="hljs-string">INFO</span><br>    <span class="hljs-attr">org:</span><br>      <span class="hljs-attr">springframework:</span> <span class="hljs-string">INFO</span><br>      <span class="hljs-attr">springframework.web:</span> <span class="hljs-string">INFO</span><br>      <span class="hljs-attr">springframework.boot.autoconfigure:</span> <span class="hljs-string">WARN</span><br></code></pre></td></tr></table></figure>

<p>搞这么个配置，启动项目后的日志打印就清晰多了：</p>
<p><img src="http://blog.memory-life.xyz/image-20240607101425070.png" alt="image-20240607101425070"></p>
<h3 id="编码格式"><a href="#编码格式" class="headerlink" title="编码格式"></a>编码格式</h3><blockquote>
<p>2024 年 8 月 6 日</p>
</blockquote>
<p>配置文件里有中文字符出现编码格式错误导致项目起不来，这报错我受够了，今天解决：</p>
<p><img src="https://gitee.com/deng-2022/pictures/raw/master/images/image-20240806172106827.png" alt="image-20240806172106827"></p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs stylus"><span class="hljs-number">17</span>:<span class="hljs-number">15</span>:<span class="hljs-number">56.384</span> <span class="hljs-selector-attr">[Thread-0]</span> DEBUG org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.devtools</span><span class="hljs-selector-class">.restart</span><span class="hljs-selector-class">.classloader</span><span class="hljs-selector-class">.RestartClassLoader</span> - Created RestartClassLoader org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.devtools</span><span class="hljs-selector-class">.restart</span><span class="hljs-selector-class">.classloader</span>.RestartClassLoader@<span class="hljs-number">47</span>e2d5ed<br><span class="hljs-number">17</span>:<span class="hljs-number">15</span>:<span class="hljs-number">56.715</span> <span class="hljs-selector-attr">[restartedMain]</span> ERROR org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.SpringApplication</span> - Application run failed<br>org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.error</span><span class="hljs-selector-class">.YAMLException</span>: java<span class="hljs-selector-class">.nio</span><span class="hljs-selector-class">.charset</span><span class="hljs-selector-class">.MalformedInputException</span>: Input length = <span class="hljs-number">1</span><br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.reader</span><span class="hljs-selector-class">.StreamReader</span><span class="hljs-selector-class">.update</span>(StreamReader<span class="hljs-selector-class">.java</span>:<span class="hljs-number">218</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.reader</span><span class="hljs-selector-class">.StreamReader</span><span class="hljs-selector-class">.ensureEnoughData</span>(StreamReader<span class="hljs-selector-class">.java</span>:<span class="hljs-number">176</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.reader</span><span class="hljs-selector-class">.StreamReader</span><span class="hljs-selector-class">.ensureEnoughData</span>(StreamReader<span class="hljs-selector-class">.java</span>:<span class="hljs-number">171</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.reader</span><span class="hljs-selector-class">.StreamReader</span><span class="hljs-selector-class">.peek</span>(StreamReader<span class="hljs-selector-class">.java</span>:<span class="hljs-number">126</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.scanner</span><span class="hljs-selector-class">.ScannerImpl</span><span class="hljs-selector-class">.scanToNextToken</span>(ScannerImpl<span class="hljs-selector-class">.java</span>:<span class="hljs-number">1218</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.scanner</span><span class="hljs-selector-class">.ScannerImpl</span><span class="hljs-selector-class">.fetchMoreTokens</span>(ScannerImpl<span class="hljs-selector-class">.java</span>:<span class="hljs-number">329</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.scanner</span><span class="hljs-selector-class">.ScannerImpl</span><span class="hljs-selector-class">.checkToken</span>(ScannerImpl<span class="hljs-selector-class">.java</span>:<span class="hljs-number">251</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.parser</span>.ParserImpl<span class="hljs-variable">$ParseImplicitDocumentStart</span><span class="hljs-selector-class">.produce</span>(ParserImpl<span class="hljs-selector-class">.java</span>:<span class="hljs-number">214</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.parser</span><span class="hljs-selector-class">.ParserImpl</span><span class="hljs-selector-class">.peekEvent</span>(ParserImpl<span class="hljs-selector-class">.java</span>:<span class="hljs-number">166</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.parser</span><span class="hljs-selector-class">.ParserImpl</span><span class="hljs-selector-class">.checkEvent</span>(ParserImpl<span class="hljs-selector-class">.java</span>:<span class="hljs-number">156</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.composer</span><span class="hljs-selector-class">.Composer</span><span class="hljs-selector-class">.checkNode</span>(Composer<span class="hljs-selector-class">.java</span>:<span class="hljs-number">93</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span><span class="hljs-selector-class">.constructor</span><span class="hljs-selector-class">.BaseConstructor</span><span class="hljs-selector-class">.checkData</span>(BaseConstructor<span class="hljs-selector-class">.java</span>:<span class="hljs-number">124</span>)<br>	at org<span class="hljs-selector-class">.yaml</span><span class="hljs-selector-class">.snakeyaml</span>.Yaml$<span class="hljs-number">1</span><span class="hljs-selector-class">.hasNext</span>(Yaml<span class="hljs-selector-class">.java</span>:<span class="hljs-number">509</span>)<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.beans</span><span class="hljs-selector-class">.factory</span><span class="hljs-selector-class">.config</span><span class="hljs-selector-class">.YamlProcessor</span><span class="hljs-selector-class">.process</span>(YamlProcessor<span class="hljs-selector-class">.java</span>:<span class="hljs-number">198</span>)<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.beans</span><span class="hljs-selector-class">.factory</span><span class="hljs-selector-class">.config</span><span class="hljs-selector-class">.YamlProcessor</span><span class="hljs-selector-class">.process</span>(YamlProcessor<span class="hljs-selector-class">.java</span>:<span class="hljs-number">166</span>)<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.env</span><span class="hljs-selector-class">.OriginTrackedYamlLoader</span><span class="hljs-selector-class">.load</span>(OriginTrackedYamlLoader<span class="hljs-selector-class">.java</span>:<span class="hljs-number">84</span>)<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.env</span><span class="hljs-selector-class">.YamlPropertySourceLoader</span><span class="hljs-selector-class">.load</span>(YamlPropertySourceLoader<span class="hljs-selector-class">.java</span>:<span class="hljs-number">50</span>)<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.context</span><span class="hljs-selector-class">.config</span><span class="hljs-selector-class">.StandardConfigDataLoader</span><span class="hljs-selector-class">.load</span>(StandardConfigDataLoader<span class="hljs-selector-class">.java</span>:<span class="hljs-number">54</span>)<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.context</span><span class="hljs-selector-class">.config</span><span class="hljs-selector-class">.StandardConfigDataLoader</span><span class="hljs-selector-class">.load</span>(StandardConfigDataLoader<span class="hljs-selector-class">.java</span>:<span class="hljs-number">36</span>)<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.context</span><span class="hljs-selector-class">.config</span><span class="hljs-selector-class">.ConfigDataLoaders</span><span class="hljs-selector-class">.load</span>(ConfigDataLoaders<span class="hljs-selector-class">.java</span>:<span class="hljs-number">107</span>)<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.boot</span><span class="hljs-selector-class">.context</span><span class="hljs-selector-class">.config</span><span class="hljs-selector-class">.ConfigDataImporter</span><span class="hljs-selector-class">.load</span>(ConfigDataImporter<span class="hljs-selector-class">.java</span>:<span class="hljs-number">128</span>)<br>.............................................<br><br></code></pre></td></tr></table></figure>

<p>原来是文件编码 File Encodings 下没有覆盖到 resources 路径下的文件，所以每次修改编码格式都不起作用的。</p>
<p><img src="https://gitee.com/deng-2022/pictures/raw/master/images/image-20240806172009453.png" alt="image-20240806172009453"></p>
<blockquote>
<p>2024 年 9 月 25 日</p>
</blockquote>
<p>今天总算真正解决了，先来看看 ChatGPT 的回答：</p>
<p>这个错误 <code>java.nio.charset.MalformedInputException: Input length = 1</code> 是因为你的 YAML 文件中的编码格式不匹配问题，通常是由于文件中的中文字符未使用正确的编码格式导致的。</p>
<h3 id="解决步骤："><a href="#解决步骤：" class="headerlink" title="解决步骤："></a>解决步骤：</h3><ol>
<li>确认 YAML 文件的编码</li>
</ol>
<p>首先，确保你的 YAML 文件是使用 <strong>UTF-8</strong> 编码保存的，因为 Spring Boot 默认使用 UTF-8 来解析配置文件。常见的编辑器中，可以检查并修改文件的编码。</p>
<ul>
<li><strong>在 IntelliJ IDEA</strong> 中：点击 <code>File -&gt; File Encoding</code>，选择 <code>UTF-8</code>。</li>
<li><strong>在 VSCode</strong> 中：点击右下角的编码格式区域，选择 <code>Save with Encoding -&gt; UTF-8</code>。</li>
</ul>
<p><img src="http://blog.memory-life.xyz/image-20240925155948563.png" alt="image-20240925155948563"></p>
<ol start="2">
<li>设置 JVM 文件编码参数</li>
</ol>
<p>你可以通过设置 JVM 的启动参数，明确指定使用 UTF-8 作为默认编码。可以在 Spring Boot 应用的启动配置中添加以下 JVM 参数：</p>
<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs routeros">bash<br><br><br>复制代码<br>-Dfile.<span class="hljs-attribute">encoding</span>=UTF-8<br></code></pre></td></tr></table></figure>

<p>对于 Maven 项目，你可以在 <code>pom.xml</code> 中的 <code>maven-surefire-plugin</code> 配置中添加：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs xml">xml复制代码<span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>maven-surefire-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">argLine</span>&gt;</span>-Dfile.encoding=UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">argLine</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20240925160034570.png" alt="image-20240925160034570"></p>
<p><strong>哟，这方法还挺好用的，已生效。</strong></p>
<p>如果你使用 Gradle，添加如下内容到 <code>build.gradle</code>：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs javascript">groovy复制代码tasks.<span class="hljs-title function_">withType</span>(<span class="hljs-params">JavaExec</span>) &#123;<br>    jvmArgs = [<span class="hljs-string">&#x27;-Dfile.encoding=UTF-8&#x27;</span>]<br>&#125;<br></code></pre></td></tr></table></figure>

<ol start="3">
<li>检查 Spring Boot 配置文件读取编码</li>
</ol>
<p>如果你已经确认文件是 UTF-8 编码，但是仍然出现问题，可以在 <code>application.properties</code> 或 <code>application.yml</code> 中明确指定文件的读取编码：</p>
<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs routeros">properties<br><br><br>复制代码<br>spring.messages.<span class="hljs-attribute">encoding</span>=UTF-8<br></code></pre></td></tr></table></figure>

<p>或者在 <code>application.yml</code> 中：</p>
<figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs nestedtext"><span class="hljs-attribute">yaml复制代码spring</span><span class="hljs-punctuation">:</span><br>  <span class="hljs-attribute">messages</span><span class="hljs-punctuation">:</span><br>    <span class="hljs-attribute">encoding</span><span class="hljs-punctuation">:</span> <span class="hljs-string">UTF-8</span><br></code></pre></td></tr></table></figure>

<ol start="4">
<li>校验 YAML 文件内容</li>
</ol>
<p>有时候，YAML 文件可能包含不可见的特殊字符（如 BOM 标记），这些字符可能会引起解析问题。可以使用文本编辑器中的显示不可见字符的功能，检查 YAML 文件头部是否有特殊字符存在。如果有，手动删除。</p>
<ol start="5">
<li>检查是否有非 UTF-8 字符</li>
</ol>
<p>如果 YAML 文件中包含某些特殊字符或非 UTF-8 编码的字符（如中文乱码），可以手动确认或使用工具将其转为 UTF-8 编码格式。</p>
<ol start="6">
<li>重新加载项目</li>
</ol>
<p>完成上述步骤后，重新启动项目，并确保配置文件被正确解析。</p>
<p>总结：</p>
<p>问题通常出在 YAML 文件的编码格式不正确。通过确认文件编码为 UTF-8、设置 JVM 启动参数、或在 Spring Boot 配置中指定编码，通常可以解决此问题。如果还是有问题，建议仔细检查文件中是否有不可见的特殊字符。</p>
<h3 id="MySQL-连接失败"><a href="#MySQL-连接失败" class="headerlink" title="MySQL 连接失败"></a>MySQL 连接失败</h3><blockquote>
<p>2024 年 8 月 14 日</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">init</span><span class="hljs-params">()</span> &#123;<br>       <span class="hljs-type">Properties</span> <span class="hljs-variable">params</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Properties</span>();<br>       <span class="hljs-type">String</span> <span class="hljs-variable">configFile</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;src/main/resources/database.properties&quot;</span>;<br>       <span class="hljs-keyword">try</span> &#123;<br>           <span class="hljs-type">FileInputStream</span> <span class="hljs-variable">in</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(configFile);<br>           params.load(in);<br>       &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>           e.printStackTrace();<br>       &#125;<br>       driver = params.getProperty(<span class="hljs-string">&quot;driver&quot;</span>);<br>       url = params.getProperty(<span class="hljs-string">&quot;url&quot;</span>);<br>       user = params.getProperty(<span class="hljs-string">&quot;user&quot;</span>);<br>       password = params.getProperty(<span class="hljs-string">&quot;password&quot;</span>);<br>   &#125;<br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20240814145419358.png" alt="image-20240814145419358"></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash">Hello world!<br>java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver<br>	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)<br>	at java.base/jdk.internal.loader.ClassLoaders<span class="hljs-variable">$AppClassLoader</span>.loadClass(ClassLoaders.java:178)<br>	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)<br>	at java.base/java.lang.Class.forName0(Native Method)<br>	at java.base/java.lang.Class.forName(Class.java:315)<br>	at com.memory.util.BaseDao.getConnection(BaseDao.java:50)<br>	at com.memory.Main.main(Main.java:20)<br></code></pre></td></tr></table></figure>

<h3 id="搭建-Spring"><a href="#搭建-Spring" class="headerlink" title="搭建 Spring"></a>搭建 Spring</h3><blockquote>
<p>2024 年 8 月 19 日</p>
</blockquote>
<p>如何搭建一个 Spring 项目？推荐阅读：<a target="_blank" rel="noopener" href="https://blog.csdn.net/tutu6663/article/details/136764745?ops_request_misc=%7B%22request_id%22:%22172406456516800227468307%22,%22scm%22:%2220140713.130102334..%22%7D&request_id=172406456516800227468307&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-136764745-null-null.142%5Ev100%5Epc_search_result_base4&utm_term=%E5%A6%82%E4%BD%95%E6%96%B0%E5%BB%BA%E4%B8%80%E4%B8%AAspring%E9%A1%B9%E7%9B%AE&spm=1018.2226.3001.4187">【spring 基础】如何搭建一个 spring 项目？-CSDN 博客</a></p>
<p>今晚，我学会了如何搭建 Spring 项目，以及三种注入 Bean 的方式：<strong>基于 XML 文件，基于 XML 文件 + 注解，完全基于注解</strong>。</p>
<p>代码已上传至：<a target="_blank" rel="noopener" href="https://gitee.com/deng-2022/shixun_life">shixun_life: 实训生活，记录我的成长 (gitee.com)</a></p>
<p>基于 XML：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserDaoImpl1</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserDao</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayHello</span><span class="hljs-params">()</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;你好啊！小一！&quot;</span>);<br><br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayHi</span><span class="hljs-params">()</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;嗨嗨！小一！&quot;</span>);<br><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserService</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserDaoImpl1 userDaoImpl1;<br>    <span class="hljs-comment">// private UserDaoImpl2 userDaoImpl2;</span><br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayHello</span><span class="hljs-params">()</span> &#123;<br>        userDaoImpl1.sayHello();<br>        <span class="hljs-comment">// userDaoImpl2.sayHello();</span><br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setUserDaoImpl1</span><span class="hljs-params">(UserDaoImpl1 userDaoImpl1)</span> &#123;<br>        <span class="hljs-built_in">this</span>.userDaoImpl1 = userDaoImpl1;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-meta">&lt;?xml version=<span class="hljs-string">&quot;1.0&quot;</span> encoding=<span class="hljs-string">&quot;UTF-8&quot;</span>?&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">&quot;http://www.springframework.org/schema/beans&quot;</span></span><br><span class="hljs-tag">       <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span><br><span class="hljs-tag">       <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;com.memory.dao.impl.UserDaoImpl1&quot;</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;userDaoImpl1&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">bean</span>&gt;</span><span class="hljs-comment">&lt;!--将UserDao1的对象配置成bean--&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;com.memory.dao.impl.UserDaoImpl2&quot;</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;userDaoImpl2&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">bean</span>&gt;</span><span class="hljs-comment">&lt;!--将UserDao2的对象配置成bean--&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;com.memory.service.impl.UserServiceImpl&quot;</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;userService&quot;</span>&gt;</span><span class="hljs-comment">&lt;!--将UserService的对象配置成bean--&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;userDaoImpl1&quot;</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">&quot;userDaoImpl1&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">property</span>&gt;</span><span class="hljs-comment">&lt;!--表示UserService的对象依赖于UserDao1对象，即告诉spring在UserService类中会用到UserDao1类型的成员变量，是spring注入的依据--&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">bean</span>&gt;</span><br><br><span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br>  <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">classPathXmlTest</span><span class="hljs-params">()</span> &#123;<br>      <span class="hljs-comment">// 基于xml配置方式的创建容器的方法</span><br>      <span class="hljs-type">ClassPathXmlApplicationContext</span> <span class="hljs-variable">ioc</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ClassPathXmlApplicationContext</span>(<span class="hljs-string">&quot;spring.xml&quot;</span>);<span class="hljs-comment">// 让spring根据xml文件中的信息创建一个spring容器</span><br>      <span class="hljs-type">UserService</span> <span class="hljs-variable">bean</span> <span class="hljs-operator">=</span> ioc.getBean(UserServiceImpl.class);<span class="hljs-comment">// 在容器中获取UserService类型的bean</span><br>      bean.sayHello();<span class="hljs-comment">// 调用该实例化对象的方法</span><br>  &#125;<br></code></pre></td></tr></table></figure>

<p>基于 XML 文件 + 注解：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserDaoImpl1</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserDao</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayHello</span><span class="hljs-params">()</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;你好啊！小一！&quot;</span>);<br><br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayHi</span><span class="hljs-params">()</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;嗨嗨！小一！&quot;</span>);<br><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserService</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserDaoImpl1 userDaoImpl1;<br>    <span class="hljs-comment">// private UserDaoImpl2 userDaoImpl2;</span><br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sayHello</span><span class="hljs-params">()</span> &#123;<br>        userDaoImpl1.sayHello();<br>        <span class="hljs-comment">// userDaoImpl2.sayHello();</span><br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setUserDaoImpl1</span><span class="hljs-params">(UserDaoImpl1 userDaoImpl1)</span> &#123;<br>        <span class="hljs-built_in">this</span>.userDaoImpl1 = userDaoImpl1;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-meta">&lt;?xml version=<span class="hljs-string">&quot;1.0&quot;</span> encoding=<span class="hljs-string">&quot;UTF-8&quot;</span>?&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">&quot;http://www.springframework.org/schema/beans&quot;</span></span><br><span class="hljs-tag">       <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span><br><span class="hljs-tag">       <span class="hljs-attr">xmlns:context</span>=<span class="hljs-string">&quot;http://www.springframework.org/schema/context&quot;</span></span><br><span class="hljs-tag">       <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span><br><br><br><span class="hljs-comment">&lt;!--    表示扫描的范围，扫描在com.memory包内的所有带@Component注解的类,并将其对象交给spring管理--&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">context:component-scan</span> <span class="hljs-attr">base-package</span>=<span class="hljs-string">&quot;com.memory&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">context:component-scan</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>基于注解：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@ComponentScan(&quot;com.memory&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SpringConfig</span> &#123;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">annotationConfigTest</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">//基于配置类的创建容器的方法</span><br>    <span class="hljs-type">AnnotationConfigApplicationContext</span> <span class="hljs-variable">ioc</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);<br>    <span class="hljs-type">UserServiceImpl</span> <span class="hljs-variable">bean</span> <span class="hljs-operator">=</span> ioc.getBean(UserServiceImpl.class);<span class="hljs-comment">//在容器中获取UserService类型的bean</span><br>    bean.sayHello();<span class="hljs-comment">// 调用该实例化对象的方法</span><br><br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="JWT-实践"><a href="#JWT-实践" class="headerlink" title="JWT 实践"></a>JWT 实践</h3><blockquote>
<p>2024 年 8 月 19 日</p>
</blockquote>
<p>推荐阅读：<a target="_blank" rel="noopener" href="https://blog.csdn.net/guorui_java/article/details/115407883">什么是 JWT – JSON WEB TOKEN_jsonwebtoken-CSDN 博客</a></p>
<p>这是昨天晚上看仿 B 站视频时，学习到的 JWT 在实际业务中的使用方式：</p>
<p>1、用户登录，以 uid 和 role 为键缓存用户信息；生成 token，以 uid 和 role 为键缓存 token 信息；返回 token 值给前端。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 用户登录</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> username 账号</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> password 密码</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> CustomResponse对象</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> CustomResponse <span class="hljs-title function_">login</span><span class="hljs-params">(String username, String password)</span> &#123;<br>        <span class="hljs-type">CustomResponse</span> <span class="hljs-variable">customResponse</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">CustomResponse</span>();<br><br>        <span class="hljs-comment">//验证是否能正常登录</span><br>        <span class="hljs-comment">//将用户名和密码封装成一个类，这个类不会存明文了，将是加密后的字符串</span><br>        <span class="hljs-type">UsernamePasswordAuthenticationToken</span> <span class="hljs-variable">authenticationToken</span> <span class="hljs-operator">=</span><br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">UsernamePasswordAuthenticationToken</span>(username, password);<br><br>        <span class="hljs-comment">// 用户名或密码错误会抛出异常</span><br>        Authentication authenticate;<br>        <span class="hljs-keyword">try</span> &#123;<br>            authenticate = authenticationProvider.authenticate(authenticationToken);<br>        &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>            customResponse.setCode(<span class="hljs-number">403</span>);<br>            customResponse.setMessage(<span class="hljs-string">&quot;账号或密码不正确&quot;</span>);<br>            <span class="hljs-keyword">return</span> customResponse;<br>        &#125;<br><br>        <span class="hljs-comment">//将用户取出来</span><br>        <span class="hljs-type">UserDetailsImpl</span> <span class="hljs-variable">loginUser</span> <span class="hljs-operator">=</span> (UserDetailsImpl) authenticate.getPrincipal();<br>        <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> loginUser.getUser();<br><br>        <span class="hljs-comment">// 顺便更新redis中的数据</span><br>        redisUtil.setExObjectValue(<span class="hljs-string">&quot;user:&quot;</span> + user.getUid(), user);  <span class="hljs-comment">// 默认存活1小时</span><br><br>        <span class="hljs-comment">// 检查账号状态，1 表示封禁中，不允许登录</span><br>        <span class="hljs-keyword">if</span> (user.getState() == <span class="hljs-number">1</span>) &#123;<br>            customResponse.setCode(<span class="hljs-number">403</span>);<br>            customResponse.setMessage(<span class="hljs-string">&quot;账号异常，封禁中&quot;</span>);<br>            <span class="hljs-keyword">return</span> customResponse;<br>        &#125;<br><br>        <span class="hljs-comment">//将uid封装成一个jwttoken，同时token也会被缓存到redis中</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> jwtUtil.createToken(user.getUid().toString(), <span class="hljs-string">&quot;user&quot;</span>);<br><br>        final_map.put(<span class="hljs-string">&quot;token&quot;</span>, token);<br>        final_map.put(<span class="hljs-string">&quot;user&quot;</span>, userDTO);<br>        customResponse.setMessage(<span class="hljs-string">&quot;登录成功&quot;</span>);<br>        customResponse.setData(final_map);<br>        <span class="hljs-keyword">return</span> customResponse;<br>    &#125;<br></code></pre></td></tr></table></figure>

<p>2、根据加密主体，加密算法和密钥，生成 Token，设置其他属性并封装载荷（即声明）。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 生成token</span><br><span class="hljs-comment">    * <span class="hljs-doctag">@param</span> uid 用户id</span><br><span class="hljs-comment">    * <span class="hljs-doctag">@param</span> role 用户角色 user/admin</span><br><span class="hljs-comment">    * <span class="hljs-doctag">@return</span> token</span><br><span class="hljs-comment">    */</span><br>   <span class="hljs-keyword">public</span> String <span class="hljs-title function_">createToken</span><span class="hljs-params">(String uid, String role)</span> &#123;<br>       <span class="hljs-comment">// uuid 加密算法 密钥 时间戳</span><br>       <span class="hljs-type">String</span> <span class="hljs-variable">uuid</span> <span class="hljs-operator">=</span> getUUID();<br>       <span class="hljs-type">SignatureAlgorithm</span> <span class="hljs-variable">signatureAlgorithm</span> <span class="hljs-operator">=</span> SignatureAlgorithm.HS256;<br>       <span class="hljs-type">SecretKey</span> <span class="hljs-variable">secretKey</span> <span class="hljs-operator">=</span> getTokenSecret();<br>       <span class="hljs-type">long</span> <span class="hljs-variable">nowMillis</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>       <span class="hljs-type">Date</span> <span class="hljs-variable">now</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(nowMillis);<br>       <span class="hljs-type">long</span> <span class="hljs-variable">expMillis</span> <span class="hljs-operator">=</span> nowMillis + JwtUtil.JWT_TTL;<br>       <span class="hljs-type">Date</span> <span class="hljs-variable">expDate</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(expMillis);<br><br>       <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> Jwts.builder()<br>               .setId(uuid)    <span class="hljs-comment">// 随机id，用于生成无规则token</span><br>               .setSubject(uid)    <span class="hljs-comment">// 加密主体</span><br>               .claim(<span class="hljs-string">&quot;role&quot;</span>, role)    <span class="hljs-comment">// token角色参数 user/admin 用于区分普通用户和管理员</span><br>               .setIssuer(<span class="hljs-string">&quot;https://api.teriteri.fun&quot;</span>)      <span class="hljs-comment">// 发行方  都是用来验证token合法性的，可以不设置，</span><br>               .setAudience(<span class="hljs-string">&quot;https://www.teriteri.fun&quot;</span>)    <span class="hljs-comment">// 接收方  本项目也没有额外用来验证合法性的逻辑</span><br>               .signWith(secretKey, signatureAlgorithm)<br>               .setIssuedAt(now)<br>               .setExpiration(expDate)<br>               .compact();<br><br>       <span class="hljs-keyword">try</span> &#123;<br>           <span class="hljs-comment">//缓存token信息，管理员和用户之间不要冲突</span><br>           redisUtil.setExValue(<span class="hljs-string">&quot;token:&quot;</span> + role + <span class="hljs-string">&quot;:&quot;</span> + uid, token, JwtUtil.JWT_TTL, TimeUnit.MILLISECONDS);<br>       &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>           log.error(<span class="hljs-string">&quot;存储redis数据异常&quot;</span>, e);<br>       &#125;<br>       <span class="hljs-keyword">return</span> token;<br>   &#125;<br></code></pre></td></tr></table></figure>

<p>3、生成 Token 密钥：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">  * 获取token密钥</span><br><span class="hljs-comment">  * <span class="hljs-doctag">@return</span> 加密后的token密钥</span><br><span class="hljs-comment">  */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> SecretKey <span class="hljs-title function_">getTokenSecret</span><span class="hljs-params">()</span> &#123;<br>     <span class="hljs-type">byte</span>[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);<br>     <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SecretKeySpec</span>(encodeKey, <span class="hljs-number">0</span>, encodeKey.length, <span class="hljs-string">&quot;HmacSHA256&quot;</span>);<br> &#125;<br></code></pre></td></tr></table></figure>

<p>4、校验 Token，由传入的 Token 获取 Claim 信息，再根据 Claim 中的 uid 和 role 获取缓存的 Token，比较是否相同。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 校验传送来的token和缓存的token是否一致</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> token token</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> true/false</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">verifyToken</span><span class="hljs-params">(String token)</span> &#123;<br>        <span class="hljs-type">Claims</span> <span class="hljs-variable">claims</span> <span class="hljs-operator">=</span> getAllClaimsFromToken(token);<br>        <span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> == claims) &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br>        &#125;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">uid</span> <span class="hljs-operator">=</span> claims.getSubject();<br>        String role;<br>        <span class="hljs-keyword">if</span> (claims.containsKey(<span class="hljs-string">&quot;role&quot;</span>)) &#123;<br>            role = claims.get(<span class="hljs-string">&quot;role&quot;</span>).toString();<br>        &#125; <span class="hljs-keyword">else</span> &#123;<br>            role = <span class="hljs-string">&quot;&quot;</span>;<br>        &#125;<br>        String cacheToken;<br>        <span class="hljs-keyword">try</span> &#123;<br>            cacheToken = String.valueOf(redisUtil.getValue(<span class="hljs-string">&quot;token:&quot;</span> + role + <span class="hljs-string">&quot;:&quot;</span> + uid));<br>        &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>            cacheToken = <span class="hljs-literal">null</span>;<br>            log.error(<span class="hljs-string">&quot;获取不到缓存的token&quot;</span>, e);<br>        &#125;<br>        <span class="hljs-keyword">return</span> StringUtils.equals(token, cacheToken);<br>    &#125;<br></code></pre></td></tr></table></figure>

<p>5、根据密钥和 Token 值即可获取所有 Claim 信息。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 获取Claims信息</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> token token</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> token的claims</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Claims <span class="hljs-title function_">getAllClaimsFromToken</span><span class="hljs-params">(String token)</span> &#123;<br>        <span class="hljs-keyword">if</span> (StringUtils.isEmpty(token)) &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>        &#125;<br>        Claims claims;<br>        <span class="hljs-keyword">try</span> &#123;<br>            claims = Jwts.parserBuilder()<br>                    .setSigningKey(getTokenSecret())<br>                    .build()<br>                    .parseClaimsJws(token)<br>                    .getBody();<br>        &#125; <span class="hljs-keyword">catch</span> (ExpiredJwtException eje) &#123;<br>            claims = <span class="hljs-literal">null</span>;<br><span class="hljs-comment">//            log.error(&quot;获取token信息异常，jwt已过期&quot;);</span><br>        &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>            claims = <span class="hljs-literal">null</span>;<br><span class="hljs-comment">//            log.error(&quot;获取token信息失败&quot;, e);</span><br>        &#125;<br>        <span class="hljs-keyword">return</span> claims;<br>    &#125;<br></code></pre></td></tr></table></figure>

<p>6、Token 验证失败，则说明 Token 已过期需重新登录；根据 uid 和 role 查询到的用户信息不匹配，则会登录失败。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><code class="hljs java">  <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * token 认证过滤器，任何请求访问服务器都会先被这里拦截验证token合法性</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> request</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> response</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> filterChain</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@throws</span> ServletException</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@throws</span> IOException</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doFilterInternal</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@NotNull</span> HttpServletResponse response, <span class="hljs-meta">@NotNull</span> FilterChain filterChain)</span> <span class="hljs-keyword">throws</span> ServletException, IOException &#123;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> request.getHeader(<span class="hljs-string">&quot;Authorization&quot;</span>);<br><br>        <span class="hljs-keyword">if</span> (!StringUtils.hasText(token) || !token.startsWith(<span class="hljs-string">&quot;Bearer &quot;</span>)) &#123;<br>            <span class="hljs-comment">// 通过开放接口过滤器后，如果没有可解析的token就放行</span><br>            filterChain.doFilter(request, response);<br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br><br>        token = token.substring(<span class="hljs-number">7</span>);<br><br>        <span class="hljs-comment">// 解析token</span><br>        <span class="hljs-type">boolean</span> <span class="hljs-variable">verifyToken</span> <span class="hljs-operator">=</span> jwtUtil.verifyToken(token);<br>        <span class="hljs-keyword">if</span> (!verifyToken) &#123;<br><span class="hljs-comment">//            log.error(&quot;当前token已过期&quot;);</span><br>            response.addHeader(<span class="hljs-string">&quot;message&quot;</span>, <span class="hljs-string">&quot;not login&quot;</span>); <span class="hljs-comment">// 设置响应头信息，给前端判断用</span><br>            response.setStatus(<span class="hljs-number">403</span>);<br><span class="hljs-comment">//            throw new AuthenticationException(&quot;当前token已过期&quot;);</span><br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">userId</span> <span class="hljs-operator">=</span> JwtUtil.getSubjectFromToken(token);<br>        <span class="hljs-type">String</span> <span class="hljs-variable">role</span> <span class="hljs-operator">=</span> JwtUtil.getClaimFromToken(token, <span class="hljs-string">&quot;role&quot;</span>);<br><br>        <span class="hljs-comment">// 从redis中获取用户信息</span><br>        <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> redisUtil.getObject(<span class="hljs-string">&quot;security:&quot;</span> + role + <span class="hljs-string">&quot;:&quot;</span> + userId, User.class);<br><br>        <span class="hljs-keyword">if</span> (user == <span class="hljs-literal">null</span>) &#123;<br><span class="hljs-comment">//            log.error(&quot;用户未登录&quot;);</span><br>            response.addHeader(<span class="hljs-string">&quot;message&quot;</span>, <span class="hljs-string">&quot;not login&quot;</span>); <span class="hljs-comment">// 设置响应头信息，给前端判断用</span><br>            response.setStatus(<span class="hljs-number">403</span>);<br><span class="hljs-comment">//            throw new AuthenticationException(&quot;用户未登录&quot;);</span><br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br><br>        <span class="hljs-comment">// 存入SecurityContextHolder，这里建议只供读取uid用，其中的状态等非静态数据可能不准，所以建议redis另外存值</span><br>        <span class="hljs-type">UserDetailsImpl</span> <span class="hljs-variable">loginUser</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UserDetailsImpl</span>(user);<br>        <span class="hljs-type">UsernamePasswordAuthenticationToken</span> <span class="hljs-variable">authenticationToken</span> <span class="hljs-operator">=</span><br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">UsernamePasswordAuthenticationToken</span>(loginUser, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>);<br>        SecurityContextHolder.getContext().setAuthentication(authenticationToken);<br><br>        <span class="hljs-comment">// 放行</span><br>        filterChain.doFilter(request, response);<br>    &#125;<br></code></pre></td></tr></table></figure>

<p>8、总而言之，使用 JWT 是在校验用户身份前，先校验是否携带了正确的 Token，再校验用户身份是否存在。</p>
<h3 id="构建-Spring-Boot-失败"><a href="#构建-Spring-Boot-失败" class="headerlink" title="构建 Spring Boot 失败"></a>构建 Spring Boot 失败</h3><blockquote>
<p>2024 年 8 月 20 日</p>
</blockquote>
<p><img src="http://blog.memory-life.xyz/image-20240820211756592.png" alt="image-20240820211756592"></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/dsy665412/p/15516254.html">Initialization failed for ‘https:start.spring.io’ Please check URL, network and proxy settings. - 小洋 ssr - 博客园 (cnblogs.com)</a></p>
<p>我也不知道怎么搞的，总之把那个 Server URL 改成阿里云的以后又改回来，莫名其妙就行了。</p>
<h3 id="Spring-Boot-初始框架"><a href="#Spring-Boot-初始框架" class="headerlink" title="Spring Boot 初始框架"></a>Spring Boot 初始框架</h3><blockquote>
<p>2024 年 8 月 20 日</p>
</blockquote>
<p>今晚完善了自己的 Spring Boot 初始框架：<a target="_blank" rel="noopener" href="https://gitee.com/deng-2022/memory-springboot-init">memory-springboot-init: Spring Boot 初始框架 (gitee.com)</a></p>
<p>以后会慢慢学习和完善。</p>
<p>我想要做一个练习：<a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_52010229/article/details/139416060">前后端不分离与前后端分离的 Java Web 开发对比介绍_java 前后端不分离-CSDN 博客</a></p>
<p>快速地构建一个 Spring Boot 项目，确保导入以下依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>		<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.projectlombok<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>lombok<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">optional</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">optional</span>&gt;</span><br>		<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span><br>		<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>			<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-thymeleaf<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>		<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>做好相关配置 appication.xml：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">spring-demo</span><br>  <span class="hljs-attr">server:</span><br>    <span class="hljs-attr">port:</span> <span class="hljs-number">8080</span><br>  <span class="hljs-attr">thymeleaf:</span><br>    <span class="hljs-attr">cache:</span> <span class="hljs-literal">false</span><br></code></pre></td></tr></table></figure>

<p>新增 Controller 层，处理 Web 请求：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Controller</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HelloController</span> &#123;<br>    <span class="hljs-meta">@GetMapping(&quot;/hello&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">sayHello</span><span class="hljs-params">(Model model)</span> &#123;<br>        model.addAttribute(<span class="hljs-string">&quot;message&quot;</span>, <span class="hljs-string">&quot;Hello, World!&quot;</span>);<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>新增 hello.html：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span> /&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Title<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">th:text</span>=<span class="hljs-string">&quot;$&#123;message&#125;&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>最终项目结构：</p>
<p><img src="http://blog.memory-life.xyz/image-20240820214137674.png" alt="image-20240820214137674"></p>
<p>启动项目，访问：<a target="_blank" rel="noopener" href="http://localhost:8080/hello">Title</a>，得到以下结果：</p>
<p><img src="http://blog.memory-life.xyz/image-20240820214210911.png" alt="image-20240820214210911"></p>
<h3 id="二维码生成"><a href="#二维码生成" class="headerlink" title="二维码生成"></a>二维码生成</h3><blockquote>
<p>2024 年 8 月 27 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_52258054/article/details/140633220">使用 JAVA 代码实现生成二维码_java 生成二维码-CSDN 博客</a></p>
<p>导入依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.google.zxing<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>core<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.4.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span> <span class="hljs-comment">&lt;!-- 或者更高版本 --&gt;</span><br>	<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>	<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.google.zxing<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>javase<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>		<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.4.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span> <span class="hljs-comment">&lt;!-- 或者更高版本 --&gt;</span><br>	<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>生成二维码的工具类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">QRCodeGenerator</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 永久二维码</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> text</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> width</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> height</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> filePath</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">generateQRCodeImage</span><span class="hljs-params">(String text, <span class="hljs-type">int</span> width, <span class="hljs-type">int</span> height, String filePath)</span> &#123;<br>        <span class="hljs-keyword">try</span> &#123;<br>            <span class="hljs-type">QRCodeWriter</span> <span class="hljs-variable">qrCodeWriter</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">QRCodeWriter</span>();<br>            Map&lt;EncodeHintType, Object&gt; hints = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);<br>            <span class="hljs-type">BitMatrix</span> <span class="hljs-variable">bitMatrix</span> <span class="hljs-operator">=</span> qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height, hints);<br><br>            <span class="hljs-type">BufferedImage</span> <span class="hljs-variable">bufferedImage</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BufferedImage</span>(width, height, BufferedImage.TYPE_INT_RGB);<br>            bufferedImage.createGraphics();<br><br>            <span class="hljs-type">Graphics2D</span> <span class="hljs-variable">graphics</span> <span class="hljs-operator">=</span> (Graphics2D) bufferedImage.getGraphics();<br>            graphics.setColor(java.awt.Color.WHITE);<br>            graphics.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, width, height);<br>            graphics.setColor(java.awt.Color.BLACK);<br><br>            <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; width; i++) &#123;<br>                <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; j &lt; height; j++) &#123;<br>                    <span class="hljs-keyword">if</span> (bitMatrix.get(i, j)) &#123;<br>                        graphics.fillRect(i, j, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);<br>                    &#125;<br>                &#125;<br>            &#125;<br><br>            ImageIO.write(bufferedImage, <span class="hljs-string">&quot;png&quot;</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">File</span>(filePath));<br>        &#125; <span class="hljs-keyword">catch</span> (WriterException | IOException e) &#123;<br>            e.printStackTrace();<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>新增一个 Controller 控制器处理请求：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;/api/common&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CommonController</span> &#123;<br><br>    <span class="hljs-meta">@Value(&quot;$&#123;backend.path&#125;&quot;)</span><br>    <span class="hljs-keyword">private</span> String path;<br><br>    <span class="hljs-meta">@GetMapping(&quot;/QRCode&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">save</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">try</span> &#123;<br>            <span class="hljs-type">String</span> <span class="hljs-variable">QR_CODE_IMAGE_PATH</span> <span class="hljs-operator">=</span> path + <span class="hljs-string">&quot;MyQRCode.png&quot;</span>; <span class="hljs-comment">// 生成的二维码图片保存路径</span><br>            <span class="hljs-type">String</span> <span class="hljs-variable">QR_CODE_TEXT</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;https://baidu.com&quot;</span>; <span class="hljs-comment">// 二维码内容，可以是URL等任意文本</span><br>            QRCodeGenerator.generateQRCodeImage(QR_CODE_TEXT, <span class="hljs-number">350</span>, <span class="hljs-number">350</span>, QR_CODE_IMAGE_PATH);<br><br>            <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;创建成功&quot;</span>;<br>        &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>            log.error(e.toString());<br>            <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;接口异常&quot;</span>;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>打开 Postman 或者 IDEA 自带的 http 请求调试工具，构建请求并发送：</p>
<p><img src="http://blog.memory-life.xyz/image-20240827184928087.png" alt="image-20240827184928087"></p>
<p><img src="http://blog.memory-life.xyz/image-20240827184953421.png" alt="image-20240827184953421"></p>
<p>如上，请求处理完成，成功生成二维码：</p>
<p><img src="http://blog.memory-life.xyz/image-20240827185043950.png" alt="image-20240827185043950"></p>
<p>手机扫码，即可跳转至百度首页：</p>
<p><img src="http://blog.memory-life.xyz/image-20240827185117749.png" alt="image-20240827185117749"></p>
<h3 id="EasyExcel"><a href="#EasyExcel" class="headerlink" title="EasyExcel"></a>EasyExcel</h3><blockquote>
<p>2024 年 9 月 2 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://github.com/alibaba/easyexcel/tree/master">alibaba/easyexcel: 快速、简洁、解决大文件内存溢出的 java 处理 Excel 工具 (github.com)</a></p>
<p><a target="_blank" rel="noopener" href="https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read">读 Excel | Easy Excel 官网 (alibaba.com)</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/xiyang_1990/article/details/138334389">一文带你彻底玩转 EasyExcel：导入导出 excel 数据起飞_easyexcel 导入-CSDN 博客</a></p>
<p>我十分看好这个博客，介绍得相当清晰条理，介绍了 Excel 提供的监听器、注解、数据导入导出以及格式转换等内容，很值得学习。</p>
<p><img src="C:/Users/Lenovo/AppData/Roaming/Typora/typora-user-images/image-20240902210643568.png" alt="image-20240902210643568"></p>
<blockquote>
<p>2024 年 9 月 5 日</p>
</blockquote>
<p>历时三天，总算有时间能聊聊如何使用 EasyExcel 实现表格的导入导出功能，Demo 代码已经写完了，顺便把曹老板也教会了。</p>
<p>导出功能实现，Controller 层新增接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 导出 Excel 表格</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> path</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@PostMapping(&quot;/excel/write&quot;)</span><br><span class="hljs-keyword">public</span> BaseResponse&lt;Boolean&gt; <span class="hljs-title function_">writeExcel</span><span class="hljs-params">(<span class="hljs-meta">@RequestParam</span> String path)</span> &#123;<br>    <span class="hljs-comment">// controller 层校验参数</span><br>    ThrowUtils.throwIf(StringUtils.isAnyBlank(path), ErrorCode.PARAMS_ERROR);<br><br>    <span class="hljs-comment">// 执行导出</span><br>    <span class="hljs-type">Boolean</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> userService.writeExcel(path);<br>    <span class="hljs-keyword">return</span> ResultUtils.success(result);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>导出表格，业务逻辑：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 导出表格</span><br><span class="hljs-comment">    *</span><br><span class="hljs-comment">    * <span class="hljs-doctag">@param</span> path 文件路径</span><br><span class="hljs-comment">    */</span><br>   Boolean <span class="hljs-title function_">writeExcel</span><span class="hljs-params">(String path)</span>;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br>  <span class="hljs-keyword">public</span> Boolean <span class="hljs-title function_">writeExcel</span><span class="hljs-params">(String path)</span> &#123;<br>      <span class="hljs-comment">// 1.定义文件路径</span><br>      <span class="hljs-type">String</span> <span class="hljs-variable">fullPath</span> <span class="hljs-operator">=</span> DOWNLOAD_PATH + USER_TABLE_NAME;<br>      <span class="hljs-comment">// 2.检查文件是否存在</span><br>      <span class="hljs-built_in">this</span>.isExist(fullPath);<br>      <span class="hljs-comment">// 3.根据路径导出</span><br>      List&lt;UserVO&gt; userList = <span class="hljs-built_in">this</span>.listUserByPage();<br>      EasyExcel.write(fullPath, User.class).sheet(USER_SHEET_NAME).doWrite(userList);<br><br>      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>  &#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 检查文件是否存在</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> fullPath</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">isExist</span><span class="hljs-params">(String fullPath)</span> &#123;<br>        <span class="hljs-type">File</span> <span class="hljs-variable">file</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">File</span>(fullPath);<br>        <span class="hljs-keyword">if</span> (file.exists()) &#123;<br>            <span class="hljs-comment">// 文件存在，则删除</span><br>            <span class="hljs-type">boolean</span> <span class="hljs-variable">isDeleted</span> <span class="hljs-operator">=</span> file.delete();<br>            <span class="hljs-keyword">if</span> (!isDeleted) &#123;<br>                <span class="hljs-comment">// 删除文件失败的处理</span><br>                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;删除文件失败: &quot;</span> + fullPath);<br>            &#125;<br>            <span class="hljs-comment">// 可以在这里添加日志或其他操作，表明文件已被删除</span><br>            System.out.println(<span class="hljs-string">&quot;文件已成功删除: &quot;</span> + fullPath);<br>        &#125;<br><br>        <span class="hljs-comment">// 文件不存在或已被删除，则尝试创建其父目录</span><br>        <span class="hljs-type">File</span> <span class="hljs-variable">parentDir</span> <span class="hljs-operator">=</span> file.getParentFile();<br>        <span class="hljs-keyword">if</span> (!parentDir.exists() &amp;&amp; !parentDir.mkdirs()) &#123;<br>            <span class="hljs-comment">// 创建目录失败的处理</span><br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;创建父级目录失败: &quot;</span> + parentDir.getAbsolutePath());<br>        &#125;<br>        <span class="hljs-comment">// 只有在成功创建目录（或目录已存在）时才打印日志</span><br>        <span class="hljs-keyword">if</span> (parentDir.exists()) &#123;<br>            <span class="hljs-comment">// 可以在这里添加日志或其他操作，表明目录已存在或已被创建</span><br>            System.out.println(<span class="hljs-string">&quot;父级目录已存在或已成功创建: &quot;</span> + parentDir.getAbsolutePath());<br>        &#125;<br>    &#125;<br></code></pre></td></tr></table></figure>

<p>有个巨坑，常量 <code>doc/ownload/user.xlsx</code> 前面多写了个<code>/</code>，变成 <code>/doc/ownload/user.xlsx</code> 后问题百出。。</p>
<p>这里支持用户选择导出文件的路径，导出文件名已经在配置文件中做好配置：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 导出的表名</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Value(&quot;$&#123;file.path.userExcelTable&#125;&quot;)</span><br><span class="hljs-type">String</span> <span class="hljs-variable">userExcelTable</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;&quot;</span>;<br></code></pre></td></tr></table></figure>

<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">file:</span><br>  <span class="hljs-attr">path:</span><br>    <span class="hljs-attr">userExcelTable:</span> <span class="hljs-string">&quot;/user.xlsx&quot;</span><br></code></pre></td></tr></table></figure>

<p>再配置实体类，标记实体类和 Excel 表格列名的关系：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 用户管理</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@TableName</span> user</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@TableName(value =&quot;user&quot;)</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 用户名</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@TableId(type = IdType.ASSIGN_ID)</span><br>    <span class="hljs-meta">@ExcelProperty(&quot;id&quot;)</span><br>    <span class="hljs-keyword">private</span> Long id;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 登录名</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@ExcelProperty(&quot;登录名&quot;)</span><br>    <span class="hljs-keyword">private</span> String userAccount;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 角色名称</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@ExcelProperty(&quot;角色名称&quot;)</span><br>    <span class="hljs-keyword">private</span> String userRole;<br><br>    .............................................<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 是否删除</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@TableLogic</span><br>    <span class="hljs-meta">@ExcelIgnore</span><br>    <span class="hljs-keyword">private</span> Integer isDeleted;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 创建时间</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@JsonFormat(pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;, timezone = &quot;GMT+8&quot;)</span><br>    <span class="hljs-meta">@ExcelIgnore</span><br>    <span class="hljs-keyword">private</span> Date createTime;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 更新时间</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@JsonFormat(pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;, timezone = &quot;GMT+8&quot;)</span><br>    <span class="hljs-meta">@ExcelIgnore</span><br>    <span class="hljs-keyword">private</span> Date updateTime;<br><br>    <span class="hljs-meta">@TableField(exist = false)</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> <span class="hljs-variable">serialVersionUID</span> <span class="hljs-operator">=</span> <span class="hljs-number">1L</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>Controoler 层接受 @RequestParam 传参，尝试发送请求：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">POST http://localhost:8101/api/user/excel/write?path=doc<br>Content-Type: application/x-www-form-urlencoded<br></code></pre></td></tr></table></figure>

<p><img src="C:/Users/Lenovo/AppData/Roaming/Typora/typora-user-images/image-20240905163442236.png" alt="image-20240905163442236"></p>
<p>显然，导出表是成功的：</p>
<p><img src="http://blog.memory-life.xyz/image-20240905163549534.png" alt="image-20240905163549534"></p>
<p>在指定导出目录下，可以找到已经成功导出的文件：</p>
<p><img src="http://blog.memory-life.xyz/image-20240905163750774.png" alt="image-20240905163750774"></p>
<p><img src="http://blog.memory-life.xyz/image-20240905163757164.png" alt="image-20240905163757164"></p>
<h3 id="Postman"><a href="#Postman" class="headerlink" title="Postman"></a>Postman</h3><blockquote>
<p>2024 年 9 月 5 日</p>
</blockquote>
<p>下午尝试用 Postman 测试导入 Excel 文件功能时，却发现连基本的请求都发不出去”：</p>
<p><img src="http://blog.memory-life.xyz/image-20240905171834528.png" alt="image-20240905171834528"></p>
<p>特么竟然是请求 url 不规范，就不应该在前面加什么 http://，已解决，太坑了。</p>
<p>导入功能实现，Controller 层新增接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 导入 Excel 表格</span><br><span class="hljs-comment">    *</span><br><span class="hljs-comment">    * <span class="hljs-doctag">@param</span> multipartFile</span><br><span class="hljs-comment">    * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">    */</span><br>   <span class="hljs-meta">@PostMapping(&quot;/excel/read&quot;)</span><br>   <span class="hljs-keyword">public</span> BaseResponse&lt;Boolean&gt; <span class="hljs-title function_">readExcel</span><span class="hljs-params">(<span class="hljs-meta">@RequestPart(&quot;file&quot;)</span> MultipartFile multipartFile)</span> &#123;<br>       <span class="hljs-comment">// controller 层校验参数</span><br>       ThrowUtils.throwIf(multipartFile == <span class="hljs-literal">null</span>, ErrorCode.PARAMS_ERROR);<br><br>       <span class="hljs-comment">// 执行导入</span><br>       <span class="hljs-type">Boolean</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> userService.readExcel(multipartFile);<br>       <span class="hljs-keyword">return</span> ResultUtils.success(result);<br>   &#125;<br></code></pre></td></tr></table></figure>

<p>注意接收参数，待会儿使用 Postman 测试接口时要注意，这个问题在上半年实习那会儿，导入导出表格的时候学到了。</p>
<p>导入表格的业务逻辑：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 导入表格</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> multipartFile</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment"> */</span><br>Boolean <span class="hljs-title function_">readExcel</span><span class="hljs-params">(MultipartFile multipartFile)</span>;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br>   <span class="hljs-keyword">public</span> Boolean <span class="hljs-title function_">readExcel</span><span class="hljs-params">(MultipartFile multipartFile)</span> &#123;<br>       <span class="hljs-comment">// 1.转换文件为字节流</span><br>       InputStream inputStream;<br>       <span class="hljs-keyword">try</span> &#123;<br>           inputStream = multipartFile.getInputStream();<br>       &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>       &#125;<br>       <span class="hljs-comment">// 2.执行文件处理逻辑</span><br>       EasyExcel.read(inputStream, User.class, <span class="hljs-keyword">new</span> <span class="hljs-title class_">UserExcelListener</span>()).sheet().doRead();<br>       <span class="hljs-comment">// 3.入库</span><br>       <span class="hljs-type">boolean</span> <span class="hljs-variable">saveBatch</span> <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.saveBatch(CACHED_DATA_LIST);<br>       ThrowUtils.throwIf(!saveBatch, ErrorCode.PARAMS_ERROR);<br>       <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>   &#125;<br></code></pre></td></tr></table></figure>

<p>执行读取的时候，当然需要有相应的监听器，做表格数据到实体类之间的转换：</p>
<p>实体类字段上的注释，还是这样的：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 用户管理</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@TableName</span> user</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@TableName(value =&quot;user&quot;)</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 用户名</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@TableId(type = IdType.ASSIGN_ID)</span><br>    <span class="hljs-meta">@ExcelProperty(&quot;id&quot;)</span><br>    <span class="hljs-keyword">private</span> Long id;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 登录名</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@ExcelProperty(&quot;登录名&quot;)</span><br>    <span class="hljs-keyword">private</span> String userAccount;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 角色名称</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@ExcelProperty(&quot;角色名称&quot;)</span><br>    <span class="hljs-keyword">private</span> String userRole;<br><br>    .............................................<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 是否删除</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@TableLogic</span><br>    <span class="hljs-meta">@ExcelIgnore</span><br>    <span class="hljs-keyword">private</span> Integer isDeleted;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 创建时间</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@JsonFormat(pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;, timezone = &quot;GMT+8&quot;)</span><br>    <span class="hljs-meta">@ExcelIgnore</span><br>    <span class="hljs-keyword">private</span> Date createTime;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 更新时间</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@JsonFormat(pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;, timezone = &quot;GMT+8&quot;)</span><br>    <span class="hljs-meta">@ExcelIgnore</span><br>    <span class="hljs-keyword">private</span> Date updateTime;<br><br>    <span class="hljs-meta">@TableField(exist = false)</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> <span class="hljs-variable">serialVersionUID</span> <span class="hljs-operator">=</span> <span class="hljs-number">1L</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>监听器：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 每隔100条处理下，然后清理list ，方便内存回收</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-variable">BATCH_COUNT</span> <span class="hljs-operator">=</span> <span class="hljs-number">100</span>;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 缓存的数据</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> List&lt;User&gt; CACHED_DATA_LIST = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 当读取到一行数据时，会调用这个方法，并将读取到的数据以及上下文信息作为参数传入</span><br><span class="hljs-comment"> * 可以在这个方法中对读取到的数据进行处理和操作，处理数据时要注意异常错误，保证读取数据的稳定性</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> user</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> context</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">invoke</span><span class="hljs-params">(User user, AnalysisContext context)</span> &#123;<br>    log.info(<span class="hljs-string">&quot;解析到一条数据:&#123;&#125;&quot;</span>, user);<br>    CACHED_DATA_LIST.add(user);<br>    <span class="hljs-keyword">if</span> (CACHED_DATA_LIST.size() &gt;= BATCH_COUNT) &#123;<br>        <span class="hljs-comment">// 处理缓存的数据，入库</span><br>        <span class="hljs-type">boolean</span> <span class="hljs-variable">saveBatch</span> <span class="hljs-operator">=</span> userService.saveBatch(CACHED_DATA_LIST);<br>        ThrowUtils.throwIf(!saveBatch, ErrorCode.PARAMS_ERROR);<br>        <span class="hljs-comment">// 然后清空</span><br>        CACHED_DATA_LIST.clear();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这里仅仅把最核心的处理逻辑列出来，详细的监听器代码已上传。</p>
<p><code>CACHED_DATA_LIST</code> 就是在监听器中处理表格中的数据后，收集到的用户信息集合，在业务代码中直接选择入库就可以。</p>
<p>顺便更新了 ExcelConstant 常量：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">ExcelConstant</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 导出存放路径</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-type">String</span> <span class="hljs-variable">DOWNLOAD_PATH</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;doc/download/&quot;</span>;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 用户表名</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-type">String</span> <span class="hljs-variable">USER_TABLE_NAME</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;user.xlsx&quot;</span>;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 用户 sheet</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-type">String</span> <span class="hljs-variable">USER_SHEET_NAME</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;用户信息&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>然后用 Postman 这样发送请求，上传准备好的表格文件：</p>
<p><img src="http://blog.memory-life.xyz/image-20240905215631262.png" alt="image-20240905215631262"></p>
<p><img src="http://blog.memory-life.xyz/image-20240905215637691.png" alt="image-20240905215637691"></p>
<p>当然，数据导入是很成功的：</p>
<p><img src="http://blog.memory-life.xyz/image-20240905215740452.png" alt="image-20240905215740452"></p>
<p><img src="http://blog.memory-life.xyz/image-20240905215747052.png" alt="image-20240905215747052"></p>
<h3 id="代码生成器"><a href="#代码生成器" class="headerlink" title="代码生成器"></a>代码生成器</h3><blockquote>
<p>2024 年 9 月 7 日</p>
</blockquote>
<p>这属于是给自己挖的坑了，实习期间只是稍微了解使用了下部门内部的代码生成器，结果简历上写了自己主导并参与开发，赶紧补救下：</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_32590535/article/details/140126466">SpringBoot 日常：整合属于自己的代码生成器_springboot 代码生成-CSDN 博客</a></p>
<p><a target="_blank" rel="noopener" href="https://baomidou.com/guides/new-code-generator/">代码生成器 | MyBatis-Plus (baomidou.com)</a></p>
<p>好好学一学咯。</p>
<h3 id="消息状态"><a href="#消息状态" class="headerlink" title="消息状态"></a>消息状态</h3><blockquote>
<p>2024 年 9 月 7 日</p>
</blockquote>
<p>简历上写道：设计人脉圈消息列表展示并标记消息状态（已读，未读），这个消息状态标记是怎么实现的呢，今晚借助 AI 硬生学会了。</p>
<p>我也不废话，直接给出精彩的思路，将来有时间了一定会付诸实践的：</p>
<p><strong>问</strong>：</p>
<p><strong>在设计人脉圈消息列表时，我会将消息状态（如已读、未读、待处理等）作为消息实体的一个字段来存储。</strong></p>
<p><strong>在查询消息列表时我会根据这个字段的值来过滤和展示不同的消息状态。此外我还会在数据库中为消息状态设置索引以加快查询速度。</strong></p>
<p>这样的功能怎么实现？</p>
<p>消息本身包含了发送人信息，接收人信息，状态等等，如何正确处理消息的已读状态？</p>
<p>就是说人脉圈里的消息大家都能看见，怎么样才能维护到每个用户看到的未读信息呢？</p>
<p><strong>回答</strong>：</p>
<p>实现人脉圈消息系统中消息状态的正确管理，特别是已读状态的维护，需要综合考虑数据存储、查询效率以及用户体验。以下是一个详细的实现方案：</p>
<p><strong>1. 数据库设计</strong></p>
<p>首先，确保你的消息表（假设为<code>messages</code>）包含以下关键字段：</p>
<ul>
<li><code>id</code>：消息的唯一标识符。</li>
<li><code>sender_id</code>：发送人的 ID。</li>
<li><code>content</code>：消息内容。</li>
<li><code>status</code>：消息状态，如<code>unread</code>（未读）、<code>read</code>（已读）、<code>pending</code>（待处理）等。</li>
<li><code>timestamp</code>：消息发送的时间戳。</li>
</ul>
<p>此外，为了高效处理消息的已读状态，可以引入一个额外的表（如<code>message_reads</code>）来记录哪些用户已经阅读了哪些消息。这个表可能包含以下字段：</p>
<ul>
<li><code>user_id</code>：阅读消息的用户的 ID。</li>
<li><code>message_id</code>：被阅读的消息的 ID。</li>
<li><code>read_timestamp</code>：用户阅读消息的时间戳（可选，用于更细粒度的分析）。</li>
</ul>
<p><strong>2. 已读状态的处理逻辑</strong></p>
<p><strong>2.1 发送消息</strong></p>
<p>当一条新消息被发送时，它默认被标记为<code>unread</code>状态，并存储在<code>messages</code>表中。此时，<code>message_reads</code>表中不会有关于这条消息的任何记录，因为它还没有被任何人阅读。</p>
<p><strong>2.2 读取消息</strong></p>
<p>当某个用户打开消息列表或查看特定消息时，系统需要执行以下操作：</p>
<ul>
<li><p>查询<code>messages</code>表，获取所有目标用户（即当前登录用户）相关的消息，包括未读和已读的消息。</p>
</li>
<li><p>对于每一条消息，检查</p>
<figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">message_reads</span><br></code></pre></td></tr></table></figure>

<p>表以确定该用户是否已经阅读过这条消息。</p>
<ul>
<li>如果<code>message_reads</code>表中存在相应记录，说明该消息已被当前用户阅读，<code>status</code>可以显示为<code>read</code>。</li>
<li>如果不存在记录，说明该消息对于当前用户来说是未读的，<code>status</code>显示为<code>unread</code>。</li>
</ul>
</li>
<li><p>如果用户阅读了之前未读的消息，系统应在<code>message_reads</code>表中插入一条新记录，记录该用户阅读了哪条消息以及阅读的时间。</p>
</li>
</ul>
<p><strong>2.3 更新消息状态（可选）</strong></p>
<p>在某些情况下，你可能想要在<code>messages</code>表中直接更新消息的<code>status</code>字段，以便快速过滤出所有未读消息。然而，这种方法在多人可见的人脉圈中可能不适用，因为你需要为每个用户维护独立的消息状态。不过，如果你只是想要快速获取所有用户的总体未读消息数，可以在<code>messages</code>表中维护一个总未读计数，并在用户阅读消息时更新这个计数（但这通常不是最佳实践，因为它需要处理并发更新和回滚等复杂问题）。</p>
<p><strong>3. 性能优化</strong></p>
<ul>
<li>为<code>messages</code>表和<code>message_reads</code>表的关键字段（如<code>user_id</code>、<code>message_id</code>）设置索引，以加快查询速度。</li>
<li>使用缓存来存储用户最近的未读消息列表，以减少数据库查询次数。</li>
<li>考虑使用消息队列或事件驱动架构来处理消息状态的更新，以提高系统的响应性和可扩展性。</li>
</ul>
<p><strong>4. 用户体验</strong></p>
<ul>
<li>在 UI 层面，清晰地区分已读和未读消息，如使用不同的颜色或图标。</li>
<li>提供“标记为已读”的功能，允许用户一键将所有未读消息标记为已读。</li>
<li>在用户进入消息列表时，自动加载并更新未读消息的状态。</li>
</ul>
<p>通过上述方案，你可以有效地实现人脉圈消息系统中的已读状态管理，同时保证系统的性能和用户体验。</p>
<p><strong>问</strong>：</p>
<p>我大概明白了你的意思，但还是有漏洞的。</p>
<p>如果用户读到了消息，怎么处理才能保证当前读到消息的用户，他看到的消息变成已读状态了，而不影响其他用户看到消息的状态，总不能直接修改 message 的 status 值吧。</p>
<p><strong>答</strong>：</p>
<p>您提出的问题非常关键，确实需要确保在用户阅读消息时，仅将该消息对于当前用户的状态标记为已读，而不影响其他用户对该消息的状态。在之前描述的方案中，我们并没有直接修改<code>messages</code>表中的<code>status</code>字段来标记消息为已读，而是依赖于<code>message_reads</code>表来跟踪每个用户阅读消息的情况。</p>
<p>这里是一个更详细的处理流程，以确保只有当前用户看到的消息被标记为已读：</p>
<ol>
<li>用户请求消息列表：<ul>
<li>当用户请求查看消息列表时，系统从<code>messages</code>表中检索出所有与该用户相关的消息（可能是通过某种关联表或字段来确定哪些消息是“与该用户相关的”，比如消息是发送给该用户的，或者该用户是消息所属人脉圈的一员）。</li>
<li>对于每一条检索出的消息，系统同时查询<code>message_reads</code>表，以确定该用户是否已经阅读过这条消息。</li>
<li>根据查询结果，系统构建消息列表，其中每条消息的显示状态（如未读、已读）根据<code>message_reads</code>表中的记录来确定。</li>
</ul>
</li>
<li>用户阅读特定消息：<ul>
<li>当用户点击或以其他方式选择阅读某条消息时，系统执行以下操作：<ul>
<li>首先，检查<code>message_reads</code>表，看是否已经存在该用户阅读该消息的记录。</li>
<li>如果不存在记录，说明该消息对于当前用户来说是未读的，系统应在<code>message_reads</code>表中插入一条新记录，记录该用户阅读了哪条消息以及阅读的时间。</li>
<li>插入记录后，无需修改<code>messages</code>表中的<code>status</code>字段，因为该字段可能用于表示消息在系统中的整体状态（如是否已被删除、是否等待审核等），而不是针对单个用户的阅读状态。</li>
<li>用户的阅读行为仅影响<code>message_reads</code>表，因此不会影响其他用户对该消息的阅读状态。</li>
</ul>
</li>
</ul>
</li>
<li>更新 UI：<ul>
<li>在用户阅读消息后，系统应更新用户界面，以反映该消息对于当前用户来说已经是已读状态。这通常涉及到修改前端页面上的消息显示状态，比如将未读消息的标记（如红点或粗体）移除。</li>
</ul>
</li>
<li>考虑并发和性能：<ul>
<li>在高并发的场景下，需要确保对<code>message_reads</code>表的读写操作是原子性的，以避免数据不一致的问题。这可以通过使用数据库事务、乐观锁或悲观锁等机制来实现。</li>
<li>同时，为了提高性能，可以考虑使用缓存来存储用户最近的阅读记录，减少对数据库的查询次数。但是，缓存的使用需要谨慎处理缓存失效和更新的问题，以确保数据的准确性。</li>
</ul>
</li>
</ol>
<p>通过上述流程，我们可以确保在用户阅读消息时，仅将该消息对于当前用户的状态标记为已读，而不影响其他用户对该消息的状态。</p>
<p><strong>问</strong>：</p>
<p>高并发状态下确实需要保证原子性啊，所有用户都在查找维护更新自己的消息状态，那这块儿内容又该怎么实现呢？</p>
<p><strong>答</strong>：</p>
<p>在高并发的场景下，确保对<code>message_reads</code>表的读写操作是原子性的，以防止数据不一致，是一个非常重要的任务。这里，我将详细解释如何通过数据库事务、乐观锁和悲观锁等机制来实现这一目标。</p>
<p><strong>1. 数据库事务</strong></p>
<p>数据库事务是一种机制，它确保数据库从一个一致的状态转变到另一个一致的状态。事务具有四个关键属性，即 ACID 特性：原子性（Atomicity）、一致性（Consistency）、隔离性（Isolation）、持久性（Durability）。</p>
<ul>
<li><strong>原子性</strong>：事务是数据库操作的最小工作单位，一个事务中的所有操作要么全部完成，要么全部不完成，不可分割。</li>
<li><strong>隔离性</strong>：事务之间的操作是相互隔离的，即并发执行的事务之间不会互相干扰。</li>
</ul>
<p>在<code>message_reads</code>表的场景中，每当用户阅读一条消息时，可以将插入记录到<code>message_reads</code>表的操作作为一个事务来处理。这样，即使在高并发的环境下，也能保证操作的原子性，从而避免数据不一致的问题。</p>
<p><strong>2. 乐观锁</strong></p>
<p>乐观锁是一种用于在并发控制中避免数据更新冲突的技术。它假设多用户并发的事务在处理时不会互相干扰，只有在更新数据时才会检查是否存在冲突。</p>
<p>在<code>message_reads</code>表中使用乐观锁，通常的做法是在表中添加一个版本号（version）或时间戳（timestamp）字段。每次读取记录时，都获取该字段的值。在更新记录时，检查版本号或时间戳是否发生了变化（即检查在读取记录之后是否有其他事务修改了该记录）。如果没有变化，则执行更新操作，并将版本号或时间戳加一；如果发生了变化，则说明存在冲突，此时可以根据业务需求选择重试或报错。</p>
<p><strong>3. 悲观锁</strong></p>
<p>与乐观锁不同，悲观锁假定在数据处理过程中最坏的情况，即数据在处理过程中很可能被其他事务并发修改。因此，悲观锁会在数据处理开始时就对数据加锁，以防止其他事务对数据进行修改。</p>
<p>在数据库层面，悲观锁通常通过 SQL 语句中的锁定语句（如<code>SELECT ... FOR UPDATE</code>）来实现。当执行这类语句时，数据库会对被选中的数据进行加锁，直到当前事务结束（提交或回滚）。</p>
<p>在<code>message_reads</code>表的场景中，如果需要保证极高的数据一致性，并且可以接受加锁带来的性能开销，那么可以使用悲观锁。例如，在用户阅读消息并准备更新<code>message_reads</code>表之前，先执行一个<code>SELECT ... FOR UPDATE</code>语句来锁定该消息对应的记录，然后再进行更新操作。</p>
<p><strong>总结</strong></p>
<p>在高并发的场景下，保证对<code>message_reads</code>表的读写操作是原子性的，可以通过数据库事务、乐观锁或悲观锁等机制来实现。选择哪种机制取决于具体的业务需求、系统性能要求以及可接受的数据一致性级别。在实际应用中，往往需要根据实际情况灵活选择或组合使用这些机制。</p>
<h3 id="日志打印"><a href="#日志打印" class="headerlink" title="日志打印"></a>日志打印</h3><blockquote>
<p>2024 年 10 月 5 日</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> org.slf4j.Logger;<br><span class="hljs-keyword">import</span> org.slf4j.LoggerFactory;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * ClassName: TomcatWebsocketServer</span><br><span class="hljs-comment"> * Package: com.memory.simplewebsocket.server</span><br><span class="hljs-comment"> * Description:</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@Author</span> Memorya</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@Create</span> 2024/10/5 10:11</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@Version</span> 1.0</span><br><span class="hljs-comment"> */</span><br><br><span class="hljs-meta">@Component</span><br><span class="hljs-meta">@ServerEndpoint(&quot;/chat/&#123;userId&#125;&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TomcatWebsocketServer</span> &#123;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Map&lt;Long, Session&gt; clients = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-type">Logger</span> <span class="hljs-variable">logger</span> <span class="hljs-operator">=</span> LoggerFactory.getLogger(ServerEndpoint.class);<br><br>    <span class="hljs-keyword">private</span> Long userId;<br><br> .............................................<br> &#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@OnClose</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onClose</span><span class="hljs-params">(Session session)</span> &#123;<br>     clients.remove(session);<br>     System.out.printf(<span class="hljs-string">&quot;连接关闭 ------------------&gt; %s%n&quot;</span>, session.getId());<br>     System.out.println(<span class="hljs-string">&quot;连接关闭 ------------------&gt; &quot;</span> + session.getId());<br>     logger.info(String.format(<span class="hljs-string">&quot;连接关闭 ------------------&gt; %s%n&quot;</span>, session.getId()));<br> &#125;<br></code></pre></td></tr></table></figure>

<h3 id="消费-生产"><a href="#消费-生产" class="headerlink" title="消费/生产"></a>消费/生产</h3><blockquote>
<p>2024 年 10 月 5 日</p>
</blockquote>
<p>这段代码是一个简单的阻塞队列实现，它使用了 Java 的 synchronized 关键字来保证线程安全，以及 wait()和 notifyAll()方法来实现线程间的通信和同步。<br>下面是对代码的详细解释：<br>构造函数 (MyBlockingQueue(int size))：<br>初始化队列的最大容量 maxSize。<br>创建一个 LinkedList<Object> 类型的 storage 来存储队列元素。<br>同步方法 (synchronized void put() throws InterruptedException)：<br>生产者线程调用此方法来向队列中添加元素。<br>使用 while 循环检查队列是否已满。如果队列已满，调用 wait() 方法使当前线程进入等待状态，直到有其他线程调用 notifyAll() 方法唤醒它。<br>当队列有空间时，添加一个新的元素，并调用 notifyAll() 方法通知所有等待的线程。<br>同步方法 (synchronized void take() throws InterruptedException)：<br>消费者线程调用此方法从队列中获取元素。<br>使用 while 循环检查队列是否为空。如果队列为空，调用 wait() 方法使当前线程进入等待状态，直到有其他线程调用 notifyAll() 方法唤醒它。<br>当队列中有元素时，移除并返回第一个元素，并调用 notifyAll() 方法通知所有等待的线程。<br>操作过程<br>创建一个 MyBlockingQueue 对象，指定队列的最大容量。<br>生产者线程调用 put() 方法尝试向队列中添加元素。如果队列已满，生产者线程将被阻塞，直到队列中有空间。<br>消费者线程调用 take() 方法尝试从队列中获取元素。如果队列为空，消费者线程将被阻塞，直到队列中有元素。<br>当队列中有空间时，生产者线程被唤醒，添加元素到队列，并通知所有等待的消费者线程。<br>当队列中有元素时，消费者线程被唤醒，从队列中移除并返回元素，并通知所有等待的生产者线程。<br>为什么使用 while 而不是 if<br>使用 while 循环而不是 if 语句是为了防止虚假唤醒。在多线程环境中，一个线程可能会在没有其他线程调用 notifyAll() 的情况下被唤醒，这被称为虚假唤醒。使用 while 循环可以确保线程在被唤醒后再次检查条件，如果条件不满足，它会继续等待，而不是继续执行可能导致错误的操作。<br>总结<br>这段代码实现了一个简单的阻塞队列，它通过使用 synchronized 关键字、wait()和 notifyAll()方法来保证线程安全和线程间的同步。使用 while 循环而不是 if 语句是为了防止虚假唤醒，确保线程在等待条件满足时能够正确地被阻塞，并且在条件满足后能够正确地被唤醒。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.memory.simplewebsocket.basic.produce;<br><br><span class="hljs-keyword">import</span> java.util.LinkedList;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyBlockingQueue</span> &#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-type">int</span> maxSize;<br>    <span class="hljs-keyword">private</span> LinkedList&lt;Object&gt; storage;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">MyBlockingQueue</span><span class="hljs-params">(<span class="hljs-type">int</span> size)</span> &#123;<br>        <span class="hljs-built_in">this</span>.maxSize = size;<br>        storage = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LinkedList</span>&lt;&gt;();<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">put</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException &#123;<br>        <span class="hljs-keyword">while</span> (storage.size() == maxSize) &#123;<br>            wait();<br>        &#125;<br>        storage.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Object</span>());<br>        notifyAll();<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">take</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException &#123;<br>        <span class="hljs-keyword">while</span> (storage.size() == <span class="hljs-number">0</span>) &#123;<br>            wait();<br>        &#125;<br>        System.out.println(storage.remove());<br>        notifyAll();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.memory.simplewebsocket.basic.produce;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 描述：     wait形式实现生产者消费者模式</span><br><span class="hljs-comment"> */</span><br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WaitStyle</span> &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        <span class="hljs-type">MyBlockingQueue</span> <span class="hljs-variable">myBlockingQueue</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyBlockingQueue</span>(<span class="hljs-number">10</span>);<br><br>        <span class="hljs-type">Producer</span> <span class="hljs-variable">producer</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Producer</span>(myBlockingQueue);<br>        <span class="hljs-type">Consumer</span> <span class="hljs-variable">consumer</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Consumer</span>(myBlockingQueue);<br><br>        <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(producer).start();<br>        <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(consumer).start();<br>    &#125;<br>&#125;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Producer</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> &#123;<br><br>    <span class="hljs-keyword">private</span> MyBlockingQueue storage;<br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">Producer</span><span class="hljs-params">(MyBlockingQueue storage)</span> &#123;<br>        <span class="hljs-built_in">this</span>.storage = storage;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++) &#123;<br>            <span class="hljs-keyword">try</span> &#123;<br>                storage.put();<br>            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                e.printStackTrace();<br>            &#125;<br>        &#125;<br>    &#125;<br>&#125;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Consumer</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> &#123;<br><br>    <span class="hljs-keyword">private</span> MyBlockingQueue storage;<br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">Consumer</span><span class="hljs-params">(MyBlockingQueue storage)</span> &#123;<br>        <span class="hljs-built_in">this</span>.storage = storage;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++) &#123;<br>            <span class="hljs-keyword">try</span> &#123;<br>                storage.take();<br>            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                e.printStackTrace();<br>            &#125;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="Maven-镜像失效"><a href="#Maven-镜像失效" class="headerlink" title="Maven 镜像失效"></a>Maven 镜像失效</h3><blockquote>
<p>2024 年 12 月 18 日</p>
</blockquote>
<p><img src="http://blog.memory-life.xyz/image-20241218091950295.png" alt="image-20241218091950295"></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/chanyeolchichi/article/details/129865197">Cannot resolve plugin org.springframework.boot:spring-boot-maven-plugin: 解决办法-CSDN 博客</a></p>
<p>用内部 VPN 账号连着公司的网络，看来 Maven 镜像源改换成泰豪软件部门的镜像源就行了。</p>
<h3 id="自启动"><a href="#自启动" class="headerlink" title="自启动"></a>自启动</h3><blockquote>
<p>2024 年 12 月 19 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/lps12345666/article/details/135445894">Spring Boot 应用启动时自动执行代码的五种方式_springboot 启动后执行一段代码-CSDN 博客</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LicenseServerApplication</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">ApplicationRunner</span> &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        SpringApplication.run(LicenseServerApplication.class, args);<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">(ApplicationArguments args)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-comment">// 在应用启动后执行的代码</span><br>        System.out.println(<span class="hljs-string">&quot;do something 55555555&quot;</span>);<br>        System.out.println(<span class="hljs-string">&quot;ApplicationRunner启动&quot;</span>);<br>        System.out.println(<span class="hljs-string">&quot;===============&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="Harbor"><a href="#Harbor" class="headerlink" title="Harbor"></a>Harbor</h3><blockquote>
<p>2024 年 12 月 20 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://gitee.com/project_harbor/harbor?utm_source=alading&utm_campaign=repo">harbor: Harbor 是为企业用户设计的容器镜像仓库开源项目，包括了权限管理(RBAC)、LDAP、审计、安全漏洞扫描、镜像验真、管理界面、自我注册、HA 等企业必需的功能，同时针对中国用户的特点，设计镜像复制和中文支持等功能。 (gitee.com)</a></p>
<p><img src="http://blog.memory-life.xyz/image-20241220121750325.png" alt="image-20241220121750325"></p>
<p><a target="_blank" rel="noopener" href="http://192.168.118.16:8080/harbor/projects/9/repositories/garethflowers%2Fftp-server/artifacts-tab/artifacts/sha256:c93dea23d08196bb5a4cb24dea2ab2c58ae22cf0e37c1bb4122979325a28969b">Harbor</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/yunxi115/article/details/131180880">Docker-harbor 私有仓库_harbor 和 docker 的关系-CSDN 博客</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.51cto.com/u_13303/10418105">harbor 与 docker 关系 harbor docker_colddawn 的技术博客_51CTO 博客</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.51cto.com/u_13444271/9767071?articleABtest=0">部署 Docker 私有镜像仓库 Harbor_Mingo 的技术博客_51CTO 博客</a></p>
<figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs awk">在企业项目中，常用的镜像仓库主要包括以下几种：<br><br>一、公有镜像仓库<br>Docker Hub<br>简介：Docker Hub是一个提供共享应用程序和自动化工作流工具的公共存储库。<br>功能：它允许开发者创建、测试、存储和共享Docker容器映像。用户可以使用Docker Hub来发现、共享和运行容器化应用程序。它还支持自动化构建、存储和部署，以及团队协作等功能。<br>二、私有镜像仓库（企业级镜像仓库）<br>Harbor<br>简介：Harbor是VMware公司开源的一个企业级Docker Registry项目，项目地址为https:<span class="hljs-regexp">//gi</span>thub.com<span class="hljs-regexp">/goharbor/</span>harbor。<br>功能：Harbor提供了一个安全、可信赖的仓库来存储和管理Docker镜像。它实现了基于角色的访问控制机制，通过项目来对镜像进行组织和访问权限的控制。此外，Harbor还提供了图形化的管理界面，方便用户通过浏览器来浏览和检索当前Docker镜像仓库，管理项目和命名空间。Harbor支持镜像的多用户管理、权限控制、镜像复制、镜像扫描、自动同步等功能，非常适合用于企业内部的镜像管理。<br>搭建：Harbor的搭建过程包括下载离线安装包、修改配置文件、修改Docker守护进程配置文件、启动安装等步骤。安装完成后，用户可以通过IP和端口访问Harbor的Web页面，进行镜像的上传、下载和管理。<br>JFrog Artifactory<br>简介：JFrog是一家知名的DevOps工具提供商，其Artifactory产品可以用作镜像仓库管理系统。<br>功能：Artifactory支持多种软件包格式，包括Docker镜像。它提供了强大的安全性、可伸缩性和管理功能，可以帮助开发团队更好地管理镜像。<br>Nexus Repository Manager<br>简介：Nexus Repository Manager是Sonatype公司推出的一款开源的存储管理工具。<br>功能：它支持多种包管理格式，包括Docker。Nexus提供了丰富的API和插件，可以与各种自动化工具和持续集成系统集成，方便用户进行镜像的存储和管理。<br>三、混合镜像仓库<br>除了公有镜像仓库和私有镜像仓库外，还有一些镜像仓库管理系统支持混合模式，即同时支持公有和私有镜像的存储和管理。这种混合模式可以根据企业的实际需求进行灵活配置和使用。<br><br>综上所述，企业项目在选择镜像仓库时，可以根据项目的具体需求、安全性要求、团队协作需求以及成本预算等因素进行综合考虑。Docker Hub、Harbor、JFrog Artifactory和Nexus Repository Manager等都是不错的选择，它们各有特点和优势，能够满足不同企业的需求。<br></code></pre></td></tr></table></figure>

<h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><blockquote>
<p>2024 年 12 月 20 日</p>
</blockquote>
<p>优雅拼接字符串，有哪些方法呢？</p>
<p>直接拼接：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-built_in">this</span>.param.setLicensePath(System.getProperty(<span class="hljs-string">&quot;user.dir&quot;</span>) + prop.getProperty(<span class="hljs-string">&quot;license.license-path&quot;</span>));<br></code></pre></td></tr></table></figure>

<p>设置分隔符：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-built_in">this</span>.param.setLicensePath(String.join(<span class="hljs-string">&quot;&quot;</span>, LicenseConstant.USER_DIR, prop.getProperty(<span class="hljs-string">&quot;license.license-path&quot;</span>)));<br></code></pre></td></tr></table></figure>

<p>然而，更常见的情况是，如果<code>privateKeysStorePath</code>是一个文件名，而你需要将其附加到<code>userDir</code>后面形成一个完整的文件路径，那么你可能需要使用文件分隔符（在 Windows 上是<code>\</code>，在 Unix/Linux/Mac 上是<code>/</code>）。但 Java 的<code>File</code>类提供了跨平台处理文件路径的方法，因此更推荐使用<code>File.separator</code>或<code>Paths</code>类来构建路径。</p>
<p>File：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-built_in">this</span>.param.setLicensePath(<span class="hljs-keyword">new</span> <span class="hljs-title class_">File</span>(LicenseConstant.USER_DIR, prop.getProperty(<span class="hljs-string">&quot;license.license-path&quot;</span>)).getPath());<br></code></pre></td></tr></table></figure>

<p>Paths：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-built_in">this</span>.param.setLicensePath(Paths.get(LicenseConstant.USER_DIR, prop.getProperty(<span class="hljs-string">&quot;license.license-path&quot;</span>)).toString());<br></code></pre></td></tr></table></figure>

<p>这几种全部都测试过了，都能够正常拼接字符串，找到文件路径。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setParam</span><span class="hljs-params">(LicenseCreatorParamDTO param)</span> &#123;<br>        <span class="hljs-comment">// 获取配置文件</span><br>        <span class="hljs-type">Properties</span> <span class="hljs-variable">prop</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Properties</span>();<br>        <span class="hljs-keyword">try</span> &#123;<br>            prop.load(LicenseCreator.class.getResourceAsStream(<span class="hljs-string">&quot;/application.properties&quot;</span>));<br>        &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>        &#125;<br>        <span class="hljs-comment">// 读取配置文件中的参数</span><br>        <span class="hljs-built_in">this</span>.param.setPrivateAlias(prop.getProperty(<span class="hljs-string">&quot;license.private-alias&quot;</span>));<br>        <span class="hljs-built_in">this</span>.param.setKeyPass(prop.getProperty(<span class="hljs-string">&quot;license.key-pass&quot;</span>));<br>        <span class="hljs-built_in">this</span>.param.setStorePass(prop.getProperty(<span class="hljs-string">&quot;license.store-pass&quot;</span>));<br>        <span class="hljs-built_in">this</span>.param.setLicensePath(Paths.get(LicenseConstant.USER_DIR, prop.getProperty(<span class="hljs-string">&quot;license.license-path&quot;</span>)).toString());<br>        <span class="hljs-built_in">this</span>.param.setPrivateKeysStorePath(Paths.get(LicenseConstant.USER_DIR, prop.getProperty(<span class="hljs-string">&quot;license.private-keys-store-path&quot;</span>)).toString());<br>        <span class="hljs-built_in">this</span>.param.setConsumerType(prop.getProperty(<span class="hljs-string">&quot;license.consumer-type&quot;</span>));<br>        <span class="hljs-built_in">this</span>.param.setConsumerAmount(Integer.parseInt(prop.getProperty(<span class="hljs-string">&quot;license.consumer-amount&quot;</span>)));<br><br>        <span class="hljs-comment">// 扩展校验服务器硬件信息</span><br>        <span class="hljs-type">LicenseCheckModel</span> <span class="hljs-variable">serverInfos</span> <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.getServerInfos(System.getProperty(<span class="hljs-string">&quot;os.name&quot;</span>));<br>        param.setLicenseCheckModel(serverInfos);<br>    &#125;<br></code></pre></td></tr></table></figure>

<h3 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h3><blockquote>
<p>2024 年 12 月 20 日</p>
</blockquote>
<p>很早之前做过这方面的研究，上传文件什么的也还是挺容易，就是时间长了就都统统给忘记了，这东西也没法找时间回顾。</p>
<p>只能是遇着问题了，再回过头来重拾解决经验吧。</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_27702739/article/details/141806466">springboot 中文件上传到本地_springboot 上传文件到本地-CSDN 博客</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@PostMapping(&quot;/upload&quot;)</span><br>  <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">upload</span><span class="hljs-params">(MultipartFile image)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>      log.info(<span class="hljs-string">&quot;文件上传成功 &#123;&#125;&quot;</span>, image);<br>      <span class="hljs-type">String</span> <span class="hljs-variable">originalFilename</span> <span class="hljs-operator">=</span> image.getOriginalFilename();<br>      image.transferTo(<span class="hljs-keyword">new</span> <span class="hljs-title class_">File</span>(<span class="hljs-string">&quot;D:\\Project\\tellhow\\license-server\\license\\&quot;</span> + originalFilename));<br>      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>  &#125;<br></code></pre></td></tr></table></figure>

<p>Postman 发起请求，上传文件：</p>
<p><img src="http://blog.memory-life.xyz/image-20241220175242382.png" alt="image-20241220175242382"></p>
<p>本机查看指定上传目录，当然上传成功了：</p>
<p><img src="http://blog.memory-life.xyz/image-20241220175213823.png" alt="image-20241220175213823"></p>
<h3 id="定时任务"><a href="#定时任务" class="headerlink" title="定时任务"></a>定时任务</h3><blockquote>
<p>2024 年 12 月 23 日</p>
</blockquote>
<p>写一个定时任务，启动类上添加 @EnableScheduling 注解，表示支持定时任务：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-meta">@Import(CorsConfig.class)</span><br><span class="hljs-meta">@EnableConfigurationProperties(UserPasswordConfigInfo.class)</span><br><span class="hljs-meta">@ServletComponentScan(basePackages = &quot;edu.hpu.filter&quot;)</span><br><span class="hljs-meta">@EnableScheduling</span><br><span class="hljs-meta">@EnableFeignClients(basePackages = &#123;&quot;com.th.cloud.iois.feign.api&quot;&#125;)</span><br><span class="hljs-meta">@EnableDiscoveryClient</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ThIoisInspectionApplication</span> &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        SpringApplication.run(ThIoisInspectionApplication.class, args);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>编写定时任务：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 证书校验定时任务</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LicenseVerifyJob</span> &#123;<br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> LicenseVerify licenseVerify;<br><br>    <span class="hljs-meta">@Scheduled(cron = &quot;*/2 * * * * *&quot;)</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">getArticleContent</span><span class="hljs-params">()</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;定时任务执行&quot;</span> + LocalDateTime.now().format(DateTimeFormatter.ofPattern(<span class="hljs-string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>)));<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20241223110409136.png" alt="image-20241223110409136"></p>
<h3 id="日期"><a href="#日期" class="headerlink" title="日期"></a>日期</h3><blockquote>
<p>2024 年 12 月 23 日</p>
</blockquote>
<p>输出当前时间，并设置日期格式：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">System.out.println(<span class="hljs-string">&quot;当前时间: &quot;</span> + LocalDateTime.now().format(DateTimeFormatter.ofPattern(<span class="hljs-string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>)));<br></code></pre></td></tr></table></figure>

<h3 id="接口文档"><a href="#接口文档" class="headerlink" title="接口文档"></a>接口文档</h3><blockquote>
<p>2024 年 12 月 19 日</p>
</blockquote>
<p>所有模块接口的文档都拿到了，网页均保存至本地集锦。</p>
<p><img src="http://blog.memory-life.xyz/image-20241219110033332.png" alt="image-20241219110033332"></p>
<p><img src="http://blog.memory-life.xyz/image-20241219175257598.png" alt="image-20241219175257598"></p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com.cn/developer/article/1549824?from=15425">Springboot 系列（十六）你真的了解 Swagger 文档吗？-腾讯云开发者社区-腾讯云 (tencent.com.cn)</a></p>
<blockquote>
<p>2024 年 12 月 24 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://download.csdn.net/blog/column/10518436/109242948">一篇文章带你搞定 SpringBoot 整合 Swagger2_Java 开发学习最全合集-CSDN 专栏</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/2403_87916407/article/details/144478652">springboot 整合 swagger2_spring boot swagger2-CSDN 博客</a></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment">&lt;!--swagger相关--&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.springfox<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>springfox-swagger2<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;swagger-version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.springfox<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>springfox-swagger-ui<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;swagger-version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@EnableSwagger2</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SwaggerConfig</span> &#123;<br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> Docket <span class="hljs-title function_">api</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Docket</span>(DocumentationType.SWAGGER_2)<br>                .apiInfo(apiInfo())<br>                .select()<br>                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))<br>                .paths(PathSelectors.any())<br>                .build();<br>    &#125;<br><br>    <span class="hljs-keyword">private</span> ApiInfo <span class="hljs-title function_">apiInfo</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ApiInfoBuilder</span>()<br>                .title(<span class="hljs-string">&quot;许可证管理接口文档&quot;</span>)<br>                .description(<span class="hljs-string">&quot;许可证管理接口文档&quot;</span>)<br>                .version(<span class="hljs-string">&quot;1.0&quot;</span>)<br>                .build();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20241224175735003.png" alt="image-20241224175735003"></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/javaxubo/p/17626752.html">org.springframework.context.ApplicationContextException: Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerException - 哩个啷个波 - 博客园 (cnblogs.com)</a></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><code class="hljs bash">org.springframework.context.ApplicationContextException: Failed to start bean <span class="hljs-string">&#x27;documentationPluginsBootstrapper&#x27;</span>; nested exception is java.lang.NullPointerException<br>	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.24.jar:5.3.24]<br>	at org.springframework.context.support.DefaultLifecycleProcessor.access<span class="hljs-variable">$200</span>(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.24.jar:5.3.24]<br>	at org.springframework.context.support.DefaultLifecycleProcessor<span class="hljs-variable">$LifecycleGroup</span>.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.24.jar:5.3.24]<br>	at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_211]<br>	at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.24.jar:5.3.24]<br>	at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.24.jar:5.3.24]<br>	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) ~[spring-context-5.3.24.jar:5.3.24]<br>	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.24.jar:5.3.24]<br>	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.7.jar:2.7.7]<br>	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) [spring-boot-2.7.7.jar:2.7.7]<br>	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) [spring-boot-2.7.7.jar:2.7.7]<br>	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.7.7.jar:2.7.7]<br>	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) [spring-boot-2.7.7.jar:2.7.7]<br>	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) [spring-boot-2.7.7.jar:2.7.7]<br>	at com.license.server.LicenseServerApplication.main(LicenseServerApplication.java:10) [classes/:na]<br>Caused by: java.lang.NullPointerException: null<br>	at springfox.documentation.spi.service.contexts.Orderings<span class="hljs-variable">$8</span>.compare(Orderings.java:112) ~[springfox-spi-2.9.2.jar:null]<br>	at springfox.documentation.spi.service.contexts.Orderings<span class="hljs-variable">$8</span>.compare(Orderings.java:109) ~[springfox-spi-2.9.2.jar:null]<br>	at com.google.common.collect.ComparatorOrdering.compare(ComparatorOrdering.java:37) ~[guava-20.0.jar:na]<br>	at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) ~[na:1.8.0_211]<br>	at java.util.TimSort.<span class="hljs-built_in">sort</span>(TimSort.java:220) ~[na:1.8.0_211]<br>	at java.util.Arrays.<span class="hljs-built_in">sort</span>(Arrays.java:1438) ~[na:1.8.0_211]<br>	at com.google.common.collect.Ordering.sortedCopy(Ordering.java:855) ~[guava-20.0.jar:na]<br>	at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:57) ~[springfox-spring-web-2.9.2.jar:null]<br>	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper<span class="hljs-variable">$2</span>.apply(DocumentationPluginsBootstrapper.java:138) ~[springfox-spring-web-2.9.2.jar:null]<br>	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper<span class="hljs-variable">$2</span>.apply(DocumentationPluginsBootstrapper.java:135) ~[springfox-spring-web-2.9.2.jar:null]<br>	at com.google.common.collect.Iterators<span class="hljs-variable">$7</span>.transform(Iterators.java:750) ~[guava-20.0.jar:na]<br>	at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47) ~[guava-20.0.jar:na]<br>	at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47) ~[guava-20.0.jar:na]<br>	at com.google.common.collect.MultitransformedIterator.hasNext(MultitransformedIterator.java:52) ~[guava-20.0.jar:na]<br>	at com.google.common.collect.MultitransformedIterator.hasNext(MultitransformedIterator.java:50) ~[guava-20.0.jar:na]<br>	at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:249) ~[guava-20.0.jar:na]<br>	at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:209) ~[guava-20.0.jar:na]<br>	at com.google.common.collect.FluentIterable.toList(FluentIterable.java:614) ~[guava-20.0.jar:na]<br>	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.defaultContextBuilder(DocumentationPluginsBootstrapper.java:111) ~[springfox-spring-web-2.9.2.jar:null]<br>	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.buildContext(DocumentationPluginsBootstrapper.java:96) ~[springfox-spring-web-2.9.2.jar:null]<br>	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:167) ~[springfox-spring-web-2.9.2.jar:null]<br>	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.24.jar:5.3.24]<br>	... 14 common frames omitted<br></code></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/zhangjunwen6666/article/details/136537579">org.springframework.context.ApplicationContextException: Failed to start bean ‘documentationPluginsB-CSDN 博客</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@EnableSwagger2</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SwaggerConfig</span> &#123;<br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> Docket <span class="hljs-title function_">api</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Docket</span>(DocumentationType.SWAGGER_2)<br>                .apiInfo(apiInfo())<br>                .select()<br>                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))<br>                .paths(PathSelectors.any())<br>                .build();<br>    &#125;<br><br>    <span class="hljs-keyword">private</span> ApiInfo <span class="hljs-title function_">apiInfo</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ApiInfoBuilder</span>()<br>                .title(<span class="hljs-string">&quot;许可证管理接口文档&quot;</span>)<br>                .description(<span class="hljs-string">&quot;许可证管理接口文档&quot;</span>)<br>                .version(<span class="hljs-string">&quot;1.0&quot;</span>)<br>                .build();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>2024 年 12 月 25 日</p>
</blockquote>
<p>怪了，昨晚能成功启动项目了，但接口文档怎么还是访问不了呢，其他接口访问正常，看来还是整合 Swagger 失败了。</p>
<p><img src="http://blog.memory-life.xyz/image-20241225090517410.png" alt="image-20241225090517410"></p>
<p><a target="_blank" rel="noopener" href="https://download.csdn.net/blog/column/10518436/109242948">一篇文章带你搞定 SpringBoot 整合 Swagger2_Java 开发学习最全合集-CSDN 专栏</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jb51.net/program/324214skm.htm">解决 springboot 接入 springfox-swagger2 遇到的一些问题<em>java</em>脚本之家 (jb51.net)</a></p>
<p><img src="http://blog.memory-life.xyz/image-20241225093647424.png" alt="image-20241225093647424"></p>
<p>就这个文档对症下药了，果然是 Spring Boot 较高版本（2.6 以上）整合 Swagger2 就会出现报错，三种解决方案：</p>
<p>1、ShortVideoSwagger2 的配置类需要集成 WebMvcConfigurationSupport</p>
<p>2、ShortVideoSwagger2 的配置类增加@EnableWebMvc 注解</p>
<p>3、springboot 的配置增加增加一下配置</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">spring.mvc.pathmatch.matching-strategy</span>=<span class="hljs-string">ANT_PATH_MATCHER</span><br></code></pre></td></tr></table></figure>

<p>通过试验得知，这三种接口都可以解决，但是前两种是有副总用的</p>
<p>@EnableWebMvc 建议慎用，最后在非 springboot 项目中使用</p>
<p>前两种解决方案会破坏 springboot 对 springwebmvc 的自动装配，导致自定义的一些 convertor 或者 ObjectMapper 失效。</p>
<p>目前我的项目中是自定义的 ObjectMapper 失效。</p>
<p>所以最后使用第三种方式，后期的 springboot 版本的 matching-strategy 默认的改为了 PATH_PATTERN_PARSER，把它改为 ANT_PATH_MATCHER 就可以了。</p>
<p>这下子项目确实能够正常启动了，并且 Swagger 文档还特么生效了，完美解决问题，总结下 SpringBoot 集成 Swagger2 的步骤：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">swagger-version</span>&gt;</span>2.9.2<span class="hljs-tag">&lt;/<span class="hljs-name">swagger-version</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">swagger-ui.version</span>&gt;</span>1.9.6<span class="hljs-tag">&lt;/<span class="hljs-name">swagger-ui.version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.springfox<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>springfox-swagger2<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;swagger-version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.springfox<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>springfox-swagger-ui<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;swagger-version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@EnableSwagger2</span><br><span class="hljs-meta">@EnableSwaggerBootstrapUI</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SwaggerConfig</span> &#123;<br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> Docket <span class="hljs-title function_">api</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Docket</span>(DocumentationType.SWAGGER_2)<br>                .apiInfo(apiInfo())<br>                .select()<br>                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))<br>                .paths(PathSelectors.any())<br>                .build();<br>    &#125;<br><br>    <span class="hljs-keyword">private</span> ApiInfo <span class="hljs-title function_">apiInfo</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ApiInfoBuilder</span>()<br>                .title(<span class="hljs-string">&quot;许可证管理接口文档&quot;</span>)<br>                .description(<span class="hljs-string">&quot;许可证管理接口文档&quot;</span>)<br>                .version(<span class="hljs-string">&quot;1.0&quot;</span>)<br>                .build();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@Api(value = &quot;License 授权校验&quot;, tags = &quot;License 授权校验&quot;)</span><br><span class="hljs-meta">@RequestMapping(&quot;/license&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LicenseVerifyController</span> &#123;<br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> LicenseVerifyService licenseVerifyService;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * License 证书上传</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> LicenseContent</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@ApiOperation(value = &quot;License 证书上传&quot;, notes = &quot;License 证书上传&quot;)</span><br>    <span class="hljs-meta">@PostMapping(&quot;/upload&quot;)</span><br>    <span class="hljs-keyword">public</span> CommonResult&lt;Boolean&gt; <span class="hljs-title function_">upload</span><span class="hljs-params">(MultipartFile file)</span> &#123;<br><br>    ........................................<br>    &#125;<br><br>    ......................................<br> &#125;<br></code></pre></td></tr></table></figure>

<p>如果出现报错就按照上面提到的方案，配置文件里增加配置即可一步到位解决，现在接口文档总算可正常查看了。</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">spring.mvc.pathmatch.matching-strategy</span>=<span class="hljs-string">ANT_PATH_MATCHER</span><br></code></pre></td></tr></table></figure>

<p><img src="http://blog.memory-life.xyz/image-20241225094907426.png" alt="image-20241225094907426"></p>
<h3 id="代码生成"><a href="#代码生成" class="headerlink" title="代码生成"></a>代码生成</h3><blockquote>
<p>2025 年 4 月 24 日</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://baomidou.com/reference/new-code-generator-configuration/">代码生成器配置 | MyBatis-Plus (baomidou.com)</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>     FastAutoGenerator.create(<span class="hljs-string">&quot;url&quot;</span>, <span class="hljs-string">&quot;username&quot;</span>, <span class="hljs-string">&quot;password&quot;</span>)<br>             .globalConfig(builder -&gt; &#123;<br>                 builder.author(<span class="hljs-string">&quot;baomidou&quot;</span>) <span class="hljs-comment">// 设置作者</span><br>                         .enableSwagger() <span class="hljs-comment">// 开启 swagger 模式</span><br>                         .outputDir(<span class="hljs-string">&quot;D://&quot;</span>); <span class="hljs-comment">// 指定输出目录</span><br>             &#125;)<br>             .dataSourceConfig(builder -&gt;<br>                     builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -&gt; &#123;<br>                         <span class="hljs-type">int</span> <span class="hljs-variable">typeCode</span> <span class="hljs-operator">=</span> metaInfo.getJdbcType().TYPE_CODE;<br>                         <span class="hljs-keyword">if</span> (typeCode == Types.SMALLINT) &#123;<br>                             <span class="hljs-comment">// 自定义类型转换</span><br>                             <span class="hljs-keyword">return</span> DbColumnType.INTEGER;<br>                         &#125;<br>                         <span class="hljs-keyword">return</span> typeRegistry.getColumnType(metaInfo);<br>                     &#125;)<br>             )<br></code></pre></td></tr></table></figure>

<p>艹，导入坐标落下了。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.baomidou<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mybatis-plus-spring-boot3-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.5.11<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.baomidou<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mybatis-plus-generator<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.5.11<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>艹。有报错，忘导数据库依赖坐标了。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash">Exception <span class="hljs-keyword">in</span> thread <span class="hljs-string">&quot;main&quot;</span> java.lang.RuntimeException: 创建IDatabaseQuery实例出现错误:<br>	at com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder.&lt;init&gt;(ConfigBuilder.java:124)<br>	at com.baomidou.mybatisplus.generator.AutoGenerator.execute(AutoGenerator.java:177)<br>	at com.baomidou.mybatisplus.generator.FastAutoGenerator.execute(FastAutoGenerator.java:239)<br>	at com.memory.mp.mybatisplustest.Utils.CodeGenerator2.main(CodeGenerator2.java:52)<br>Caused by: java.lang.reflect.InvocationTargetException<br>	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)<br>	at<br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>8.0.33<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span> <span class="hljs-comment">&lt;!-- or latest version --&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>添加 FreeMarker 依赖。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs bash">Exception <span class="hljs-keyword">in</span> thread <span class="hljs-string">&quot;main&quot;</span> java.lang.NoClassDefFoundError: freemarker/template/Configuration<br>	at com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine.init(FreemarkerTemplateEngine.java:42)<br>	at com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine.init(FreemarkerTemplateEngine.java:37)<br>	at com.baomidou.mybatisplus.generator.AutoGenerator.execute(AutoGenerator.java:185)<br>	at com.baomidou.mybatisplus.generator.FastAutoGenerator.execute(FastAutoGenerator.java:239)<br>	at com.memory.mp.mybatisplustest.Utils.CodeGenerator2.main(CodeGenerator2.java:52)<br>Caused by: java.lang.ClassNotFoundException: freemarker.template.Configuration<br>	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)<br>	at java.base/jdk.internal.loader.ClassLoaders<span class="hljs-variable">$AppClassLoader</span>.loadClass(ClassLoaders.java:188)<br>	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)<br>	... 5 more<br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.freemarker<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>freemarker<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.3.32<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span> <span class="hljs-comment">&lt;!-- 或最新版本 --&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>呐，执行完毕后竟然弹出文件管理器窗口，看来执行成功了。</p>
<p><img src="http://blog.memory-life.xyz/image-20250424214743537.png" alt="image-20250424214743537"></p>
<p><a target="_blank" rel="noopener" href="https://doc.xiaominfo.com/docs/quick-start">快速开始 | Knife4j (xiaominfo.com)</a></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>17<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span><br>       <span class="hljs-comment">&lt;!--swagger相关--&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">swagger-version</span>&gt;</span>2.9.2<span class="hljs-tag">&lt;/<span class="hljs-name">swagger-version</span>&gt;</span><br>       <span class="hljs-tag">&lt;<span class="hljs-name">swagger-ui.version</span>&gt;</span>1.9.6<span class="hljs-tag">&lt;/<span class="hljs-name">swagger-ui.version</span>&gt;</span><br>   <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.springfox<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>springfox-swagger2<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;swagger-version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>      <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>io.springfox<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>springfox-swagger-ui<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>          <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;swagger-version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>      <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">mvc:</span><br>    <span class="hljs-attr">pathmatch:</span><br>      <span class="hljs-attr">matching-strategy:</span> <span class="hljs-string">ant_path_matcher</span><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ApiModel(value = &quot;User对象&quot;, description = &quot;用户表&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> <span class="hljs-variable">serialVersionUID</span> <span class="hljs-operator">=</span> <span class="hljs-number">1L</span>;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 主键</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@ApiModelProperty(&quot;主键&quot;)</span><br>    <span class="hljs-meta">@TableId(value = &quot;id&quot;, type = IdType.AUTO)</span><br>    <span class="hljs-keyword">private</span> Long id;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 用户名</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@ApiModelProperty(&quot;用户名&quot;)</span><br>    <span class="hljs-keyword">private</span> String username;<br><br>.......................<br>&#125;<br></code></pre></td></tr></table></figure>

<p>代码生成成功，Controller，service，mapper 和 entity。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CodeGenerator2</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        FastAutoGenerator.create(<span class="hljs-string">&quot;jdbc:mysql://localhost:3306/duck&quot;</span>, <span class="hljs-string">&quot;root&quot;</span>, <span class="hljs-string">&quot;Dw990831&quot;</span>)<br>                .globalConfig(builder -&gt; &#123;<br>                    builder.author(<span class="hljs-string">&quot;memory&quot;</span>) <span class="hljs-comment">// 设置作者</span><br>                            .enableSwagger() <span class="hljs-comment">// 开启 swagger 模式</span><br>                            .outputDir(<span class="hljs-string">&quot;D:\\Project\\IDEA\\MybatisPlusTest\\src\\&quot;</span>); <span class="hljs-comment">// 指定输出目录</span><br>                &#125;)<br>                .dataSourceConfig(builder -&gt;<br>                        builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -&gt; &#123;<br>                            <span class="hljs-type">int</span> <span class="hljs-variable">typeCode</span> <span class="hljs-operator">=</span> metaInfo.getJdbcType().TYPE_CODE;<br>                            <span class="hljs-keyword">if</span> (typeCode == Types.SMALLINT) &#123;<br>                                <span class="hljs-comment">// 自定义类型转换</span><br>                                <span class="hljs-keyword">return</span> DbColumnType.INTEGER;<br>                            &#125;<br>                            <span class="hljs-keyword">return</span> typeRegistry.getColumnType(metaInfo);<br>                        &#125;)<br>                )<br>                .packageConfig(builder -&gt;<br>                        builder.parent(<span class="hljs-string">&quot;com.memory.generator&quot;</span>) <span class="hljs-comment">// 设置父包名</span><br>                                .moduleName(<span class="hljs-string">&quot;&quot;</span>) <span class="hljs-comment">// 设置父包模块名</span><br>                                .pathInfo(Collections.singletonMap(OutputFile.xml, <span class="hljs-string">&quot;D:\\Project\\IDEA\\MybatisPlusTest\\src\\main\\resources&quot;</span>)) <span class="hljs-comment">// 设置mapperXml生成路径</span><br>                )<br>                .strategyConfig(builder -&gt;<br>                        builder.addInclude(<span class="hljs-string">&quot;user, user2&quot;</span>) <span class="hljs-comment">// 设置需要生成的表名, 多张表之间用逗号隔开</span><br>                                .addTablePrefix(<span class="hljs-string">&quot;t_&quot;</span>, <span class="hljs-string">&quot;c_&quot;</span>) <span class="hljs-comment">// 设置过滤表前缀</span><br>                )<br>                .templateEngine(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FreemarkerTemplateEngine</span>()) <span class="hljs-comment">// 使用Freemarker引擎模板，默认的是Velocity引擎模板</span><br>                .execute();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="版本不兼容"><a href="#版本不兼容" class="headerlink" title="版本不兼容"></a>版本不兼容</h3><blockquote>
<p>2025 年 4 月 24 日</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs bash">***************************<br>APPLICATION FAILED TO START<br>***************************<br><br>Description:<br><br>An attempt was made to call a method that does not exist. The attempt was made from the following location:<br><br>    org.springframework.plugin.core.support.AbstractTypeAwareSupport<span class="hljs-variable">$BeansOfTypeTargetSource</span>.&lt;init&gt;(AbstractTypeAwareSupport.java:135)<br><br>The following method did not exist:<br><br>    <span class="hljs-string">&#x27;void org.springframework.util.Assert.notNull(java.lang.Object)&#x27;</span><br><br>The calling method<span class="hljs-string">&#x27;s class, org.springframework.plugin.core.support.AbstractTypeAwareSupport$BeansOfTypeTargetSource, was loaded from the following location:</span><br><span class="hljs-string"></span><br><span class="hljs-string">    jar:file:/D:/softWare/Maven/apache-maven-3.6.1/mvn_resp/org/springframework/plugin/spring-plugin-core/1.2.0.RELEASE/spring-plugin-core-1.2.0.RELEASE.jar!/org/springframework/plugin/core/support/AbstractTypeAwareSupport$BeansOfTypeTargetSource.class</span><br><span class="hljs-string"></span><br><span class="hljs-string">The called method&#x27;</span>s class, org.springframework.util.Assert, is available from the following locations:<br><br>    jar:file:/D:/softWare/Maven/apache-maven-3.6.1/mvn_resp/org/springframework/spring-core/6.2.5/spring-core-6.2.5.jar!/org/springframework/util/Assert.class<br><br>The called method<span class="hljs-string">&#x27;s class hierarchy was loaded from the following locations:</span><br><span class="hljs-string"></span><br><span class="hljs-string">    org.springframework.util.Assert: file:/D:/softWare/Maven/apache-maven-3.6.1/mvn_resp/org/springframework/spring-core/6.2.5/spring-core-6.2.5.jar</span><br><span class="hljs-string"></span><br><span class="hljs-string"></span><br><span class="hljs-string">Action:</span><br><span class="hljs-string"></span><br><span class="hljs-string">Correct the classpath of your application so that it contains compatible versions of the classes org.springframework.plugin.core.support.AbstractTypeAwareSupport$BeansOfTypeTargetSource and org.springframework.util.Assert</span><br><span class="hljs-string"></span><br><span class="hljs-string"></span><br><span class="hljs-string">Process finished with exit code 1</span><br></code></pre></td></tr></table></figure>

<p>根据你的 <code>pom.xml</code> 配置，你使用的是 <strong>Spring Boot 3.4.4</strong>（基于 Spring 6），但遇到了 <code>spring-plugin-core</code> 版本不兼容的问题。以下是完整的修复方案：</p>
<hr>
<p><strong>问题根源</strong></p>
<ol>
<li><strong><code>spring-plugin-core</code> 1.2.0.RELEASE</strong> 是为 Spring 5 设计的，不兼容 Spring 6。</li>
<li><strong>Spring 6</strong> 移除了 <code>Assert.notNull(Object)</code>，改为 <code>Assert.notNull(Object, String)</code>，导致 <code>NoSuchMethodError</code>。</li>
</ol>
<hr>
<p><strong>解决方案</strong></p>
<ol>
<li><strong>升级 <code>spring-plugin-core</code> 到 Spring 6 兼容版本</strong></li>
</ol>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.plugin<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-plugin-core<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span> <span class="hljs-comment">&lt;!-- 支持 Spring 6 --&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>又有新问题：</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs stylus"><br>org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.beans</span><span class="hljs-selector-class">.factory</span><span class="hljs-selector-class">.BeanCreationException</span>: Error creating bean with name <span class="hljs-string">&#x27;documentationPluginsBootstrapper&#x27;</span>: Resolution of declared constructors on bean Class <span class="hljs-selector-attr">[springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper]</span> from ClassLoader <span class="hljs-selector-attr">[jdk.internal.loader.ClassLoaders$AppClassLoader@5e481248]</span> failed<br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.beans</span><span class="hljs-selector-class">.factory</span><span class="hljs-selector-class">.annotation</span><span class="hljs-selector-class">.AutowiredAnnotationBeanPostProcessor</span><span class="hljs-selector-class">.determineCandidateConstructors</span>(AutowiredAnnotationBeanPostProcessor<span class="hljs-selector-class">.java</span>:<span class="hljs-number">384</span>) ~<span class="hljs-selector-attr">[spring-beans-6.2.5.jar:6.2.5]</span><br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.beans</span><span class="hljs-selector-class">.factory</span><span class="hljs-selector-class">.support</span><span class="hljs-selector-class">.AbstractAutowireCapableBeanFactory</span><span class="hljs-selector-class">.determineConstructorsFromBeanPostProcessors</span>(AbstractAutowireCapableBeanFactory<span class="hljs-selector-class">.java</span>:<span class="hljs-number">1320</span>) ~<span class="hljs-selector-attr">[spring-beans-6.2.5.jar:6.2.5]</span><br>	at org<span class="hljs-selector-class">.springframework</span><span class="hljs-selector-class">.beans</span><span class="hljs-selector-class">.factory</span><span class="hljs-selector-class">.support</span><span class="hljs-selector-class">.AbstractAutowireCapableBeanFactory</span><span class="hljs-selector-class">.createBeanInstance</span>(AbstractAutowireCapableBeanFactory<span class="hljs-selector-class">.java</span>:<span class="hljs-number">1215</span>) ~<span class="hljs-selector-attr">[spring-beans-6.2.5.jar:6.2.5]</span><br></code></pre></td></tr></table></figure>

<p>使用 <strong>Springfox Swagger</strong>（<code>springfox.documentation</code>）时与 <strong>Spring Boot 3</strong> 不兼容。Springfox Swagger 2.x 是为 Spring Boot 2.x 设计的，而 Spring Boot 3 需要迁移到 <strong>SpringDoc OpenAPI</strong>（官方推荐替代方案）。</p>
<p><a target="_blank" rel="noopener" href="https://doc.xiaominfo.com/docs/quick-start">快速开始 | Knife4j (xiaominfo.com)</a></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.github.xiaoymin<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>knife4j-openapi3-jakarta-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.4.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># springdoc-openapi项目配置</span><br><span class="hljs-attr">springdoc:</span><br>  <span class="hljs-attr">swagger-ui:</span><br>    <span class="hljs-attr">path:</span> <span class="hljs-string">/swagger-ui.html</span><br>    <span class="hljs-attr">tags-sorter:</span> <span class="hljs-string">alpha</span><br>    <span class="hljs-attr">operations-sorter:</span> <span class="hljs-string">alpha</span><br>  <span class="hljs-attr">api-docs:</span><br>    <span class="hljs-attr">path:</span> <span class="hljs-string">/v3/api-docs</span><br>  <span class="hljs-attr">group-configs:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">group:</span> <span class="hljs-string">&quot;default&quot;</span><br>      <span class="hljs-attr">paths-to-match:</span> <span class="hljs-string">&quot;/**&quot;</span><br>      <span class="hljs-attr">packages-to-scan:</span> <span class="hljs-string">com.xiaominfo.knife4j.demo.web</span><br></code></pre></td></tr></table></figure>

<p>总算启动接口文档成功了，都是版本兼容问题，Spring Boot，Spring 版本过高，Swagger 版本需要相应作调整。</p>
<p><a target="_blank" rel="noopener" href="http://localhost:8080/doc.html#/home">OpenAPI definition</a></p>
<p><img src="http://blog.memory-life.xyz/image-20250424221859901.png" alt="image-20250424221859901"></p>
<h3 id="空页返回"><a href="#空页返回" class="headerlink" title="空页返回"></a>空页返回</h3><blockquote>
<p>2025 年 5 月 3 日</p>
</blockquote>
<p>要正确返回一个空的分页集合（<code>Page&lt;MemberReadHistory&gt;</code>），你可以使用 Spring Data 提供的 <code>Page.empty()</code> 方法。以下是完整的实现方式：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> Page&lt;MemberReadHistory&gt; <span class="hljs-title function_">list</span><span class="hljs-params">(Integer pageNum, Integer pageSize)</span> &#123;<br>    <span class="hljs-keyword">return</span> Page.empty();<br>&#125;<br></code></pre></td></tr></table></figure>

<p>如果你需要返回一个带有分页信息的空页（保持与请求的分页参数一致），可以使用：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> Page&lt;MemberReadHistory&gt; <span class="hljs-title function_">list</span><span class="hljs-params">(Integer pageNum, Integer pageSize)</span> &#123;<br>    <span class="hljs-type">Pageable</span> <span class="hljs-variable">pageable</span> <span class="hljs-operator">=</span> PageRequest.of(pageNum, pageSize);<br>    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PageImpl</span>&lt;&gt;(Collections.emptyList(), pageable, <span class="hljs-number">0</span>);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>其中：</p>
<p><code>Page.empty()</code> 是最简单的空页表示。</p>
<p><code>PageImpl</code>构造函数允许指定：</p>
<ul>
<li>空列表作为内容 (<code>Collections.emptyList()</code>)</li>
<li>分页信息 (<code>PageRequest</code>)</li>
<li>总记录数（0 表示空集合）</li>
</ul>
<p>选择哪种方式取决于你的具体需求。如果不需要保留分页信息，第一种方式更简洁；如果需要保持分页元数据，使用第二种方式。</p>
<h3 id="多线程优化"><a href="#多线程优化" class="headerlink" title="多线程优化"></a>多线程优化</h3><blockquote>
<p>2025 年 7 月 29 日</p>
</blockquote>
<p>批处理是优化写接口性能的一种方式，而当接口出现性能问题时，多线程技术永远是解决问题的一大利器，不过许多人对多线程的适用并不熟练，这里先来说明异步和并发（并行）的区别。</p>
<ul>
<li>异步：将对应的任务递交给其他线程后，不需要等待结果返回，可以直接对外响应；</li>
<li>并发：通过多条线程来提升效率，提交任务的主线程需要等待结果返回，只是优化性能。</li>
</ul>
<p>异步和并发两个概念彼此兼容，所以许多人有点犯迷糊，结合生活来理解，比如现在我要搬一百块砖头，可是我一个人搬的太慢了，所以想着多喊几个人来帮忙，于是我找到<code>X、Y、Z</code>，并叫它们一起来搬砖提升效率，这时我会等它们搬完，这就是并发的概念。</p>
<blockquote>
<p>如果我找到<code>X、Y、Z</code>把搬砖任务丢给了它们，不管它们有没有搬完，然后我就自己走了，这就是异步的概念。</p>
</blockquote>
<p>综上，当接口写入性能较差时，咱们确实可以通过多线程来优化性能，可到底要用多线程来并发处理，还是用它来异步处理呢？这就取决于你实际的业务场景，来看例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">java 体验AI代码助手 代码解读复制代码<span class="hljs-keyword">public</span> String <span class="hljs-title function_">writeBigData</span><span class="hljs-params">(List&lt;Panda&gt; pandas)</span> &#123;<br>    pandaService.writePandas(pandas);<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写入成功&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这是一个写数据的接口，假设外部传入了<code>10W</code>条熊猫数据需要落库，单线程处理的效率过低，这时用多线程优化，可以这么写：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> String <span class="hljs-title function_">writeBigData</span><span class="hljs-params">(List&lt;Panda&gt; pandas)</span> &#123;<br>    threadPool.submit(() -&gt; &#123;<br>        pandaService.writePandas(pandas);<br>    &#125;);<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写入成功&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这段代码中，主线程将写熊猫数据的任务，丢给线程池后立马返回了，这是典型的异步写法，再来看例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> String <span class="hljs-title function_">writeBigData</span><span class="hljs-params">(List&lt;Panda&gt; pandas)</span> &#123;<br>    <span class="hljs-comment">// 先对数据进行切分，分割为1000一批的数据</span><br>    List&lt;List&lt;Panda&gt;&gt; partitions = ListUtils.partition(pandas, <span class="hljs-number">1000</span>);<br>    <span class="hljs-comment">// 定义计数器</span><br>    <span class="hljs-type">AtomicInteger</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AtomicInteger</span>(<span class="hljs-number">0</span>);<br>    <span class="hljs-comment">// 循环所有批次，将任务提交给线程池</span><br>    <span class="hljs-keyword">for</span> (List&lt;Panda&gt; partition : partitions) &#123;<br>        threadPool.submit(() -&gt; &#123;<br>            pandaService.writePandas(partition);<br>            count.incrementAndGet();<br>        &#125;);<br>    &#125;<br>    <br>    <span class="hljs-comment">// 模拟阻塞（实际要通过Future来阻塞等待执行结果）</span><br>    <br>    <span class="hljs-comment">// 如果写入成功的批数，等于划分出的批数，返回写入成功</span><br>    <span class="hljs-keyword">if</span> (count.get() == partitions.size()) &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写入成功&quot;</span>;<br>    &#125;<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写入失败&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>再来看这种写法，首先对传入的熊猫集合进行了分批，将数据分为多个<code>1000</code>条的小批次，而后遍历拆分后的批次列表，将拆分的每批数据都丢给了线程池去执行。再来看外部的主线程，任务投递给线程池后并未立马返回，而是在等待所有批次的执行结果，只有当所有批次都完成写入后，才真正向调用方返回了写入成功。</p>
<p><strong>异步是任务的执行状态，并发是任务执行过程中的具体策略。</strong></p>
<h3 id="跨域"><a href="#跨域" class="headerlink" title="跨域"></a>跨域</h3><blockquote>
<p>2025 年 7 月 29 日</p>
</blockquote>
<p>跨域问题的产生背景是浏览器的同源策略（<code>Same-Origin Policy</code>），同源策略是浏览器的一种安全机制，它限制了从同一个源加载的文档或脚本，如何与来自不同源的资源进行交互。这里的源（<code>origin</code>）是指协议、域名、端口号组成的唯一标识，如果出现两个地址，这三者组合起来对比不一致，就代表着不同源。</p>
<p>如何解决不同源的跨域问题呢？也很简单，在<code>SpringBoot</code>里通过一个<code>@CrossOrigin</code>注解就能搞定，如果嫌挨个<code>Controller</code>加注解麻烦，也可以通过实现<code>WebMvcConfigurer</code>接口，并重写<code>addCorsMappings()</code>方法来全局配置跨域，即：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span>  <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CorsConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> &#123;  <br>  <br>    <span class="hljs-meta">@Override</span>  <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addCorsMappings</span><span class="hljs-params">(CorsRegistry registry)</span> &#123;  <br>        registry<br>        <span class="hljs-comment">// 代表允许所有路径跨域</span><br>        .addMapping(<span class="hljs-string">&quot;/**&quot;</span>)<br>                <span class="hljs-comment">// 允许来自http://xxx.com的请求跨域  </span><br>                .allowedOrigins(<span class="hljs-string">&quot;http://xxx.com&quot;</span>)<br>                <span class="hljs-comment">// 允许这些HTTP方法跨域</span><br>                .allowedMethods(<span class="hljs-string">&quot;GET&quot;</span>, <span class="hljs-string">&quot;POST&quot;</span>, <span class="hljs-string">&quot;PUT&quot;</span>, <span class="hljs-string">&quot;DELETE&quot;</span>)<br>                <span class="hljs-comment">// 允许任何头跨域</span><br>                .allowedHeaders(<span class="hljs-string">&quot;*&quot;</span>)<br>                <span class="hljs-comment">// 允许发送Cookie</span><br>                .allowCredentials(<span class="hljs-literal">true</span>)<br>                <span class="hljs-comment">// 预检请求的缓存时间（秒）</span><br>                .maxAge(<span class="hljs-number">3600</span>);<br>    &#125;  <br>&#125;<br></code></pre></td></tr></table></figure>

<p>当然，解决跨域问题的本质，是往响应头里塞几个字段，即：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">response.setHeader(<span class="hljs-string">&quot;Access-Control-Allow-Origin&quot;</span>, <span class="hljs-string">&quot;*&quot;</span>);  <br>response.setHeader(<span class="hljs-string">&quot;Access-Control-Allow-Methods&quot;</span>, <span class="hljs-string">&quot;POST, GET, OPTIONS, DELETE&quot;</span>);  <br>response.setHeader(<span class="hljs-string">&quot;Access-Control-Max-Age&quot;</span>, <span class="hljs-string">&quot;3600&quot;</span>);  <br>response.setHeader(<span class="hljs-string">&quot;Access-Control-Allow-Headers&quot;</span>, <span class="hljs-string">&quot;x-requested-with, authorization&quot;</span>); <br></code></pre></td></tr></table></figure>

<p>因此，<strong>只要能够改写响应头的地方都可以解决跨域问题</strong>，所以市面上才会出现那么多解决跨域的方式：</p>
<ul>
<li>使用<code>@CrossOrigin</code>注解解决跨域问题；</li>
<li>实现<code>WebMvcConfigurer</code>接口解决跨域问题；</li>
<li>通过自定义<code>Filter</code>过滤器解决跨域问题；</li>
<li>通过<code>Response</code>对象实现跨域问题；</li>
<li>通过实现<code>ResponseBodyAdvice</code>接口解决跨域问题；</li>
<li>通过设置<a target="_blank" rel="noopener" href="https://juejin.cn/post/7112826654291918855#heading-11">Nginx配置</a>解决跨域问题；</li>
<li>……</li>
</ul>
<p>这么多种跨域解决方案听起来很唬人，但只要是服务端解决的跨域问题，殊途同归都是改写响应头罢了。</p>
<h1 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h1>
                
              </div>
            
            <hr/>
            <div>
              <!-- 文章页面 - 底部标签 -->
<div class="post-metas my-3">
  
  <div class="post-meta mr-3 d-flex align-items-center">
    <i class="iconfont icon-category"></i>
    <!-- 归档页 - 文章列表 -->


<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/blog/categories/%E4%BB%A3%E7%A0%81%E9%94%8B%E8%8A%92/" class="category-chain-item">代码锋芒</a>
  
  

      </span>
    
  
</span>

  </div>
   
  <div class="post-meta">
    <i class="iconfont icon-tags"></i>
    
    <a href="/blog/tags/%E7%BB%8F%E9%AA%8C/">#经验</a>
    
    <a href="/blog/tags/Redis/">#Redis</a>
    
    <a href="/blog/tags/%E9%A1%B9%E7%9B%AE/">#项目</a>
    
    <a href="/blog/tags/SpringBoot/">#SpringBoot</a>
    
    <a href="/blog/tags/Mybatis/">#Mybatis</a>
    
    <a href="/blog/tags/%E9%98%BF%E9%87%8C%E4%BA%91OSS/">#阿里云OSS</a>
    
  </div>
  
</div>


              <!-- 文章页面 - 底部标签下方 -->

  
  <div class="license-box my-3">
    <div class="license-title">
      <div>凤凰涅槃：Spring Boot 开发之路上的坎坷与成长</div>
      <div>https://test.atomgit.net/blog/2023/07/06/凤凰涅槃：Spring Boot 开发之路上的坎坷与成长/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>Memory</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2023年7月6日</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>更新于</div>
          <div>2025年8月4日</div>
        </div>
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/blog/2023/07/11/%E4%BB%8EIP%E5%88%B0Ping%E5%91%BD%E4%BB%A4%EF%BC%9A%E4%BA%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E8%BF%9E%E6%8E%A5%E7%9A%84%E5%9F%BA%E7%A1%80/" title="从IP到Ping命令：了解计算机网络连接的基础">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">从IP到Ping命令：了解计算机网络连接的基础</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/blog/2023/07/06/%E6%89%93%E9%80%A0%E6%B5%81%E7%95%85%E7%BC%96%E7%A0%81%E4%B9%8B%E6%97%85%EF%BC%9AIntelliJ%20IDEA%20%E7%9A%84%E9%AB%98%E6%95%88%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7%E4%B8%8E%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E4%BC%98%E5%8C%96/" title="打造流畅编码之旅：IntelliJ IDEA 的高效使用技巧与工作流程优化">
                        <span class="hidden-mobile">打造流畅编码之旅：IntelliJ IDEA 的高效使用技巧与工作流程优化</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            <!-- 文章页 - 评论上方 -->

  
  
    <article id="comments">
      
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://cdn.jsdelivr.net/gh/HCLonely/Valine@latest/dist/Valine.min.js', function() {
        var options = Object.assign(
          {"appId":"T1hUHH9Ks1ggG9DBE3HgUPpV-gzGzoHsz","appKey":"XFkgzzvzNo3J3uNCbBiSOFKc","path":"window.location.pathname","placeholder":"填取QQ号和邮箱后评论，支持读取头像及消息提醒，留下点什么叭... ᶘ ᵒᴥᵒᶅ","avatar":"Gravatar","meta":["nick","mail","link"],"requiredFields":[],"pageSize":10,"lang":"zh-CN","highlight":true,"recordIP":false,"serverURLs":"https://t1huhh9k.lc-cn-n1-shared.com","emojiCDN":null,"emojiMaps":null,"enableQQ":true,"master":"8c9b6d6637904da62d74d019cc6982de","friends":"0188229a9cfc284176ea9135cb9514cc","visitor":true},
          {
            el: "#valine",
            path: window.location.pathname,
            app_id: "",
            app_key: "",
            placeholder: "填取QQ号和邮箱后评论，支持读取头像及消息提醒，留下点什么叭... ᶘ ᵒᴥᵒᶅ",
            avatar: "Gravatar",
            meta: ["nick","mail","link"],
            pageSize: "10",
            lang: "zh-CN",
            highlight: true,
            recordIP: false,
            serverURLs: "https://t1huhh9k.lc-cn-n1-shared.com",

            // 设置Bilibili表情包地址
            emojiCDN: '//i0.hdslb.com/bfs/emote/', 
            // 表情title和图片映射
            emojiMaps: {
                "tv_白眼": "48f75163437445665a9be80bb316e4cb252c5415.gif@48w_48h.webp",
                "tv_doge": "302d6c88c63ed162c81a49cafe7ed2709e6fb955.gif@48w_48h.webp",
                "tv_坏笑": "5d2572efd09aab5dde9e2a198bb3f9ac1e2a982e.gif@48w_48h.webp",
                "tv_难过": "9c6b41008a67755410f712334c64313df5f91b3f.gif@48w_48h.webp",
                "tv_生气": "1902a5a2df5b5c931d88c12f0feb264b1e109d0d.gif@48w_48h.webp",
                "tv_委屈": "af5a5853edb43a8178a8cb5df707fa5e88143699.gif@48w_48h.webp",
                "tv_斜眼笑": "c66568b471192ca1f62f6ed4384dc1b283ab7508.gif@48w_48h.webp",
                "tv_呆": "d3fa91e4db9215eb1e20ab9da44f1214aa4bda7b.gif@48w_48h.webp",
                "tv_发怒": "3959eb81b952e4fa8d269d98f9e3639172d84073.gif@48w_48h.webp",
                "tv_呕吐": "db58e9442aae26694af18cc1683607cca3a16763.gif@48w_48h.webp",
                "tv_思考": "b63f9146bfd985af014f8d6d4bdb498805be48f9.gif@48w_48h.webp",
                "tv_微笑": "b98656855d782f61cb8edc7f7fca6563ecafff7e.gif@48w_48h.webp",
                "tv_疑问": "fce1b1a0f3b0e39a2dc16a18508dba7b91e929f4.gif@48w_48h.webp",
                "tv_大哭": "cba61f05f3039b02a7ffc0dfcd9d7995df9fdd74.gif@48w_48h.webp",
                "tv_鼓掌": "be106e6b265883a9f28fbe10f7b765701e2618d4.gif@48w_48h.webp",
                "tv_抠鼻": "696d9f93e722144dc2a78aeffc569418fdf3d565.gif@48w_48h.webp",
                "tv_亲亲": "3534ea44ab74bd20352b88c245a06c4b4c46d271.gif@48w_48h.webp",
                "tv_调皮": "fcd967395fd14e4dd5829fa7e8a967ce23205e52.gif@48w_48h.webp",
                "tv_笑哭": "1c2fd1e8c9dde12812f86e5d4cbddd8993d98082.gif@48w_48h.webp",
                "tv_晕": "030040ec5c9ddc9e3d067658c4139e7314ab42f8.gif@48w_48h.webp",
                "tv_点赞": "30ecff401245fb56bcc1cf588d1809ac1ab1607c.gif@48w_48h.webp",
                "tv_害羞": "411a3e459e8580f5bfd9f639a408247c4b509935.gif@48w_48h.webp",
                "tv_睡着": "3c8b5e293261287a6203597e29b3de07df4d18c6.gif@48w_48h.webp",
                "tv_色": "a0c6d99ab0ab63b8648f5283ff72cec04b604828.gif@48w_48h.webp",
                "tv_吐血": "e17e4539e169d14a3389ff147afea760cebe5de5.gif@48w_48h.webp",
                "tv_无奈": "eb4cb5f07cfd177c7e6a7914316717e56d9cc1d0.gif@48w_48h.webp",
                "tv_再见": "344f61609ecce2008520dc8a977b6169215748a9.gif@48w_48h.webp",
                "tv_流汗": "390bccec65eaff536bd5bb2a0c5b8b0bdea47334.gif@48w_48h.webp",
                "tv_偷笑": "7f11e6f7f63e79112b833bd41fa13a83d7cd8474.gif@48w_48h.webp",
                "tv_抓狂":"a476b93ecd8e94ac3257323fd822f91cef212de2.gif@48w_48h.webp",
                "tv_黑人问号":"b609adf664be33224a9923262031165ae3e34cd2.gif@48w_48h.webp",
                "tv_困":"91c2bf34ecf842d7016c01d841db3d4074bd281f.gif@48w_48h.webp",
                "tv_打脸":"b0fad4856e59c1240e448437da3287bb5ce547e5.gif@48w_48h.webp",
                "tv_闭嘴":"a3fc5388b09e945be3f18fe23bfed5874a0285b7.gif@48w_48h.webp",
                "tv_鄙视":"293b5d459e6264ecf314d20937a936fa672ccd1e.gif@48w_48h.webp",
                "tv_腼腆":"30984e8264324f901d19bea85dada7103b695534.gif@48w_48h.webp",
                "tv_馋":"2525c5703c594e5f0752f68db8948773caebde47.gif@48w_48h.webp",
                "tv_可爱":"f92d20f76258bc5f33fc9d7c5e2a1d41fef19a7c.gif@48w_48h.webp",
                "tv_发财":"76131e52c9b033681b4c896c6024d29ef7ec7ec2.gif@48w_48h.webp",
                "tv_生病":"beb94829fe04f1a41bd6ca611e1f6ca9ca169afa.gif@48w_48h.webp",
                "tv_流鼻血":"8ef473f74a849420da712487b2f56ecca1f695f5.gif@48w_48h.webp",
                "tv_尴尬":"e0b84ef5ee3e5b8978e584c7c5a6550c51d15f84.gif@48w_48h.webp",
                "tv_大佬":"14ca0c05382b8741940942b2430b7a8d55c02f7e.gif@48w_48h.webp",
                "tv_流泪":"7e71cde7858f0cd50d74b0264aa26db612a8a167.png@48w_48h.webp",
                "tv_冷漠":"b9cbc755c2b3ee43be07ca13de84e5b699a3f101.png@48w_48h.webp",
                "tv_皱眉":"72ccad6679fea0d14cce648b4d818e09b8ffea2d.png@48w_48h.webp",
                "tv_鬼脸":"0ffbbddf8a94d124ca2f54b360bbc04feb6bbfea.png@48w_48h.webp",
                "tv_调侃":"4bc022533ef31544ca0d72c12c808cf4a1cce3e3.png@48w_48h.webp",
                "tv_目瞪口呆":"0b8cb81a68de5d5365212c99375e7ace3e7891b7.png@48w_48h.webp",
            },

            master: "8c9b6d6637904da62d74d019cc6982de",
            friends: "0188229a9cfc284176ea9135cb9514cc",
            tagMeta: ["博主","友人","访客"],
            visitor: true // 阅读量统计
          }
        )
        new Valine(options);
        Fluid.utils.waitElementVisible('#valine .vcontent', () => {
          var imgSelector = '#valine .vcontent img:not(.vemoji)';
          Fluid.plugins.imageCaption(imgSelector);
          Fluid.plugins.fancyBox(imgSelector);
        })
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


    </article>
  


          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      <!-- 文章页面 - 右侧目录 -->

  <aside class="sidebar" style="margin-left: -1rem">
    <!-- 文章页面 - 右侧目录上方 -->
<div id="toc">
  <p class="toc-header">
    <i class="iconfont icon-list"></i>
    <span>目录</span>
  </p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>



    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <!-- 博客 - 搜索框 -->
<div
  class="modal fade"
  id="modalSearch"
  tabindex="-1"
  role="dialog"
  aria-labelledby="ModalLabel"
  aria-hidden="true"
>
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">
          搜索
        </h4>
        <button
          type="button"
          id="local-search-close"
          class="close"
          data-dismiss="modal"
          aria-label="Close"
        >
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input
            type="text"
            id="local-search-input"
            class="form-control validate"
          />
          <label data-error="x" data-success="v" for="local-search-input"
            >关键词</label
          >
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
        <div class="modal fade" id="modalNotice" tabindex="-1" role="dialog" aria-hidden="true"
     style="font-family: 'Ma Shan Zheng', cursive; color: #ffffff;">
    <div class="modal-dialog modal-dialog-scrollable" role="document"
         style="font-family: 'Ma Shan Zheng', cursive;">
        <div class="modal-content" style="border-radius: 6px;">
            <!-- 公告头部 - 响应式设计 -->
            <div class="modal-header text-center"
                 style="padding: 1rem 1.5rem;border-bottom: 1px solid rgba(255,255,255,0.2)">
                <h4 class="modal-title w-100 font-weight-bold"
                    style="font-family: 'Microsoft YaHei', sans-serif;letter-spacing: 1px;text-shadow: 1px 1px 2px rgba(0,0,0,0.2);color: #ffffff;
                           font-size: 1.5rem; /* 默认PC端大小 */
                           /* 移动端适配 */
                           @media (max-width: 768px) {
                               font-size: 1.2rem;
                           }">
                    <div class="notice-title">
                        <i class="fas fa-bullhorn mr-2"> 围栏告示</i>
                    </div>
                    <small style="display: block; font-family: 'STKaiti', serif; font-size: 0.6em; color: #a0a0a0; margin-top: 3px;">
                        旧梦轻拾 · 谨识
                    </small>
                </h4>
            </div>

            <!-- 公告内容区 - 响应式设计 -->
            <div class="modal-body"
                 style="padding: 2rem; /* PC端 */
                        max-height: 70vh;
                        overflow-y: auto;
                        /* 移动端适配 */
                        @media (max-width: 768px) {
                            padding: 1rem;
                            max-height: 60vh;
                        }">
                <!-- 图片容器（响应式设计） -->
                <!--                <div class="announcement-hero"-->
                <!--                     style="margin-bottom: 2rem; /* PC端 */-->
                <!--                            /* 移动端适配 */-->
                <!--                            @media (max-width: 768px) {-->
                <!--                                margin-bottom: 1rem;-->
                <!--                            }">-->
                <!--                    <img src="http://blog.memory-life.icu/%E3%80%90%E5%93%B2%E9%A3%8E%E5%A3%81%E7%BA%B8%E3%80%91%E5%8A%A8%E6%BC%AB-%E5%A4%95%E9%98%B3%E9%93%81%E8%BD%A8.jpg"-->
                <!--                         alt="时光溯洄题图"-->
                <!--                         style="width: 100%; -->
                <!--                                max-height: 400px; /* PC端 */-->
                <!--                                object-fit: cover;-->
                <!--                                border-radius: 4px; -->
                <!--                                box-shadow: 0 2px 12px rgba(0,0,0,0.1);-->
                <!--                                /* 移动端适配 */-->
                <!--                                @media (max-width: 768px) {-->
                <!--                                    max-height: 30vh;-->
                <!--                                }"-->
                <!--                         loading="lazy">-->
                <!--                </div>-->

                <!-- 静态公告列表 - 响应式设计 -->
                <div class="announcement-list">
                    <div class="announcement-item"
                         style="margin-bottom: 2rem; /* PC端 */
                                padding: 2rem;
                                background: rgba(255,255,255,0.05);
                                border-radius: 8px;
                                /* 移动端适配 */
                                @media (max-width: 768px) {
                                    margin-bottom: 1rem;
                                    padding: 1rem;
                                }">
                        <h2 style="color: #d38532;
                                  font-weight: 600;
                                  font-size: 1.5rem; /* PC端 */
                                  margin-bottom: 1.2rem;
                                  letter-spacing: 0.5px;
                                  /* 移动端适配 */
                                  @media (max-width: 768px) {
                                      font-size: 1.2rem;
                                      margin-bottom: 1rem;
                                  }">
                            <i class="fas fa-calendar-check mr-2"></i>时光溯洄
                            <small style="display: block; font-size: 0.6em; color: #b8b8b8;text-align: right;">——
                                晨昏往事</small>
                        </h2>
                        <div style="color: #e0e0e0;
                                  line-height: 1.8; /* PC端 */
                                  font-size: 1.05rem;
                                  letter-spacing: 0.3px;
                                  /* 移动端适配 */
                                  @media (max-width: 768px) {
                                      line-height: 1.6;
                                      font-size: 0.95rem;
                                  }">
                            <!-- 内容保持不变 -->
                            <p style="margin-bottom: 1rem; text-indent: 2em;">你好。</p>
                            <!-- 其余段落内容... -->
                            <p style="margin-bottom: 1rem; text-indent: 2em;">
                                建站后的第813天，在百忙之中总算又能抽出时间来，对整个博客站点功能做整体的规划及优化完善。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em;">
                                在刚刚过去的几年时间里，往事皆以笔墨挥毫于时光素笺之上，数百天如一日，故事的书写从未间断过。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em; font-style: italic; color: #b8b8b8;">
                                时光悠悠，日月如流，过往的蹉跎岁月总在不经意间在心间回眸。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em; font-weight: 500; color: #ffffff;">
                                俯仰之间，轻舟已过万重山。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em; font-weight: 500; color: #ffffff;">
                                蹉跎之处，往事皆成云烟散。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em;">
                                这几年的时光过得太快了，好像过去这段岁月里的所有事情都发生在一瞬间。</p>
                            <p style="margin-bottom: 1rem; text-indent: 2em;">
                                这几年的时光同样太久了，久到那些时日里仿佛囊括了自己的整个前半生。</p>
                            <p style="margin-bottom: 0; text-align: right; font-size: 0.95rem; color: #a0a0a0;">——
                                八百余个昼夜，当时只道是地久天长，如今回望，不过浮光掠影间的一瞬。</p>
                        </div>
                    </div>

                    <!-- 第二个公告项 - 响应式设计 -->
                    <div class="announcement-item"
                         style="margin-bottom: 2rem; /* PC端 */
                                padding: 2rem;
                                background: rgba(255,255,255,0.05);
                                border-radius: 8px;
                                /* 移动端适配 */
                                @media (max-width: 768px) {
                                    margin-bottom: 1rem;
                                    padding: 1rem;
                                }">
                        <h2 style="color: #6cc70b;
                                  font-weight: 600;
                                  font-size: 1.5rem; /* PC端 */
                                  margin-bottom: 1.2rem;
                                  letter-spacing: 0.5px;
                                  /* 移动端适配 */
                                  @media (max-width: 768px) {
                                      font-size: 1.2rem;
                                      margin-bottom: 1rem;
                                  }">
                            <i class="fas fa-book-open mr-2"></i>墨痕新注
                            <small style="display: block; font-size: 0.6em; color: #b8b8b8;text-align: right;">——
                                待起之章</small>
                        </h2>
                        <div style="color: #e0e0e0;
                                  line-height: 1.8; /* PC端 */
                                  font-size: 1.05rem;
                                  letter-spacing: 0.3px;
                                  /* 移动端适配 */
                                  @media (max-width: 768px) {
                                      line-height: 1.6;
                                      font-size: 0.95rem;
                                  }">
                            <!-- 列表内容 - 响应式设计 -->
                            <ul style="padding-left: 1.8rem; /* PC端 */
                                      margin-bottom: 1.5rem;
                                      /* 移动端适配 */
                                      @media (max-width: 768px) {
                                          padding-left: 1.5rem;
                                          margin-bottom: 1.2rem;
                                      }">
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">溯源计划：</span>新增博文"溯源计划"，旨在用先进的数字技术留住过去的旧时光，让家族的记忆在数字云端生长发芽，全站博文数量已达90+。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">围栏告示：</span>自去年夏天站点新增了“评论回复”功能后，至今时隔十余月，在芒种前夕本站久违地上线“围栏告示”功能，本公告栏会不定时更新，持续跟进记录最新站点功能调整日志，更多精彩内容请敬请期待。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">图片查看：</span>最近几周时间里，考虑到图文流量费用收取问题，暂时关闭了七牛云对象存储私有空间访问权限，故博文内的图文无法正常预览，择日考虑开启访问权限。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">壁纸下载：</span>后续计划实现壁纸下载功能，均为个人收集的共150+超高分辨率精美壁纸，提供在线预览以及下载功能。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">阅读体验：</span>规划完善文章内分页功能，并提供“最近更新”的文章列表，计划收录更新率较高的文章，譬如：阳台日记、叙事之外、岁月如歌、游戏人生等等，尽可能多的提升在线浏览和阅读体验。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">友链完善：</span>持续优化完善友链页面，整理归档个人常用的高质量网站合集，无偿分享给有缘人。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">音乐餐厅：</span>历经十余小时不间断调研和尝试，本站成功上线”音乐餐厅”功能，目前仅收录了个人在QQ音乐平台积累的两份歌单，再匆忙也要记得点播一首喜欢的音乐，没有音乐熏陶的心灵终究是充满缺憾的。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">三言两语：</span>每张照片都是时光的便签，随手一贴就记下了那天，本站结合Mamos实现并注入留言板功能，随缘记录那些藏在照片背后的故事。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">旧梦轻拾：</span>更新全站博客文章所属分类，共设有代码锋芒、部署之道、旧梦轻拾、人间片刻等十余种分类。
                                </li>
                                <li style="margin-bottom: 0.8rem; /* PC端 */
                                         position: relative;
                                         list-style-type: none;
                                         /* 移动端适配 */
                                         @media (max-width: 768px) {
                                             margin-bottom: 0.6rem;
                                         }">
                                    <span style="position: absolute;
                                               left: -1.8rem; /* PC端 */
                                               color: #6cc70b;
                                               /* 移动端适配 */
                                               @media (max-width: 768px) {
                                                   left: -1.5rem;
                                               }">◆</span>
                                    <span style="font-weight: bolder;">方寸之间：</span>博客应用版发布，更便捷的阅读体验来了！现已正式推出独立的桌面应用程序与移动端应用程序，暂时通过网盘和代码库提供安装包。
                                </li>
                                <p style="margin-bottom: 0; text-align: right; font-size: 0.9rem; color: #8a8a8a;">
                                    此公告最后更新于：<span style="color: #6cc70b;">2025/09/30晚</span>
                                </p>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 公告页脚 - 响应式设计 -->
            <div class="modal-footer"
                 style="border-top: 1px solid rgba(255,255,255,0.2);
                        padding: 1rem 1.5rem; /* PC端 */
                        justify-content: space-between;
                        /* 移动端适配 */
                        @media (max-width: 768px) {
                            padding: 0.8rem;
                        }">
                <small style="color: #ffffff;
                            font-size: 0.9rem; /* PC端 */
                            /* 移动端适配 */
                            @media (max-width: 768px) {
                                font-size: 0.8rem;
                            }">
                    <i class="iconfont icon-pen"></i>
                    <i class="fas fa-info-circle mr-1"></i>芒种前夕 · 旅途第813天
                    · 2025/05/28
                </small>
                <button id="confirm-btn" type="button" class="btn btn-sm" data-dismiss="modal"
                        style="background: #a0a0a0;
                               color: #ffffff;
                               border-radius: 4px;
                               padding: 0.5rem 1.2rem; /* PC端 */
                               border: none;
                               font-size: 0.9rem;
                               /* 移动端适配 */
                               @media (max-width: 768px) {
                                   padding: 0.3rem 0.8rem;
                                   font-size: 0.85rem;
                               }">
                    <i class="fas fa-check mr-1" style="color: #ffffff;">心已阅</i>
                </button>
            </div>
        </div>
    </div>
</div>

<style>
  .notice-title {
    display: flex;
    align-items: center;
    white-space: nowrap;
  }

  .notice-title:before,
  .notice-title:after {
    content: "";
    flex: 1;
    height: 1px;
    background: #ddd; /* 横线颜色 */
    margin: 0 10px; /* 横线与文字的间距 */
  }
</style>


<style>
  /* 响应式模态框宽度 */
  @media (min-width: 992px) {
    #modalNotice .modal-dialog {
      max-width: 800px;
    }
  }

  @media (max-width: 991px) {
    #modalNotice .modal-dialog {
      margin: 0.5rem auto;
    }
  }
</style>

<script>
  // 页面加载完成后执行
  document.addEventListener('DOMContentLoaded', function () {
    // 检查是否首次访问
    if (!localStorage.getItem('hasShownPopup')) {
      // 动态加载notice.ejs内容
      fetch('_partials/notice.ejs')  // 替换为实际路径
        .then(response => response.text())
        .then(html => {
          // 插入到页面body末尾
          document.body.insertAdjacentHTML('beforeend', html);

          // 手动初始化Bootstrap模态框
          $('#modalNotice').modal('show');

          // 标记为已显示
          localStorage.setItem('hasShownPopup', 'true');

          // 监听关闭事件
          $('#modalNotice').on('hidden.bs.modal', function () {
            // 可选的后续处理
          });
        })
        .catch(error => {
          console.error('加载公告栏失败:', error);
          // 备用方案：显示简单弹窗
          const fallbackHTML = `
                <div class="modal-backdrop" style="/* 样式 */">
                    <div class="modal-content">/* 内容 */</div>
                </div>`;
          document.body.insertAdjacentHTML('beforeend', fallbackHTML);
        });
    }
  });
</script>

    

    
  </main>

  <footer>
    <!-- 博客 - 页脚 -->
<div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.fluid-dev.com/docs/guide/" target="_blank" rel="nofollow noopener"> <span>Memory | 个人文档站点</span> </a>
<i class="iconfont icon-love"></i> <a href="https://hexo.fluid-dev.com/docs/guide/" target="_blank" rel="nofollow noopener"> <span>配置指南 | Hexo Fluid 用户手册</span> </a> 
    </div>
  

  

  
    <!-- 备案信 ICP for China -->
    <div class="beian">
  <span>
    <a href="http://beian.miit.gov.cn/" target="_blank" rel="nofollow noopener">
      京ICP证123456号
    </a>
  </span>
  
    
      <span>
        <a
          href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=12345678"
          rel="nofollow noopener"
          class="beian-police"
          target="_blank"
        >
          
            <span style="visibility: hidden; width: 0">|</span>
            <img src="/blog/img/police_beian.png" alt="police-icon"/>
          
          <span>京公网安备12345678号</span>
        </a>
      </span>
    
  
</div>

  

  

</div>

<!-- 评论框美化 -->
<style>
    #comments .veditor{
        min-height: 20vh;
        background-image: url(http://blog.memory-life.icu/pathway0716.jpg);
        background-size: contain;
        background-repeat: no-repeat;
        background-position: right;
        background-color: rgba(255,255,255,0);
        resize: none;}
</style>

<script>
    //   自定义邮箱审核规则
    document.body.addEventListener('click', function(e) {
        if (e.target.classList.contains('vsubmit')) {
            const email = document.querySelector('input[type=email]');
            const nick = document.querySelector('input[name=nick]');
            const reg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
            if (!email.value || !nick.value || !reg.test(email.value)) {
                const str = `<div class="valert text-center"><div class="vtext">请填写正确的昵称和邮箱！</div></div>`;
                const vmark = document.querySelector('.vmark');
                vmark.innerHTML = str;
                vmark.style.display = 'block';
                setTimeout(function() {
                    vmark.style.display = 'none';
                    vmark.innerHTML = '';
                }, 2500);
            }
        }
    })
</script>



  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/blog/js/events.js" ></script>
<script  src="/blog/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>







  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/blog/js/local-search.js" ></script>

  <script defer src="/blog/js/leancloud.js" ></script>




    
<script src="https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>



<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/blog/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
